diff --git a/build.sh b/build.sh index ec03fc9..5060ccc 100755 --- a/build.sh +++ b/build.sh @@ -8,47 +8,47 @@ CMAKE=cmake3 if [[ $(which $CMAKE) = "" ]]; then - CMAKE=cmake # try this next + CMAKE=cmake # try this next fi if [[ $(which $CMAKE) = "" ]]; then - echo "CMake (cmake) not found. Please install before continuing." - exit + echo "CMake (cmake) not found. Please install before continuing." + exit fi # if [[ ! $(which tree) = "" ]]; then - TREE=tree + TREE=tree else - TREE="du -a " + TREE="du -a " fi # Determine operating system OSNAME=$(uname | tr '[A-Z]' '[a-z]') if [[ $OSNAME = *"darwin"* ]]; then - SHARED_LIB_NAME="libzt.dylib" - STATIC_LIB_NAME="libzt.a" - HOST_PLATFORM="macos" + SHARED_LIB_NAME="libzt.dylib" + STATIC_LIB_NAME="libzt.a" + HOST_PLATFORM="macos" fi if [[ $OSNAME = *"linux"* ]]; then - SHARED_LIB_NAME="libzt.so" - STATIC_LIB_NAME="libzt.a" - HOST_PLATFORM="linux" + SHARED_LIB_NAME="libzt.so" + STATIC_LIB_NAME="libzt.a" + HOST_PLATFORM="linux" fi # Determine and normalize machine type HOST_MACHINE_TYPE=$(uname -m) if [[ $HOST_MACHINE_TYPE = *"x86_64"* ]]; then - HOST_MACHINE_TYPE="x64" + HOST_MACHINE_TYPE="x64" fi # Determine number of cores. We'll tell CMake to use them all if [[ $OSNAME = *"darwin"* ]]; then - N_PROCESSORS=$(sysctl -n hw.ncpu) + N_PROCESSORS=$(sysctl -n hw.ncpu) fi if [[ $OSNAME = *"linux"* ]]; then - N_PROCESSORS=$(nproc --all) + N_PROCESSORS=$(nproc --all) fi # How many processor cores CMake should use during builds, @@ -63,7 +63,7 @@ function ver() printf "%02d%02d%02d%02d" ${1//./ } } if [[ (( $(ver $CMAKE_VERSION) > $(ver "3.12") )) ]]; then - BUILD_CONCURRENCY="-j $N_PROCESSORS" + BUILD_CONCURRENCY="-j $N_PROCESSORS" fi # ----------------------------------------------------------------------------- @@ -87,7 +87,7 @@ DEFAULT_HOST_BUILD_CACHE_DIR=$BUILD_CACHE_DIR/$HOST_PLATFORM-$HOST_MACHINE_TYPE gethosttype() { - echo $HOST_PLATFORM-$HOST_MACHINE_TYPE + echo $HOST_PLATFORM-$HOST_MACHINE_TYPE } # ----------------------------------------------------------------------------- @@ -100,8 +100,8 @@ gethosttype() # # Example output: # -# - Cache : libzt/cache/apple-xcframework-debug -# - Build output : libzt/dist +# - Cache : libzt/cache/apple-xcframework-debug +# - Build output : libzt/dist # # apple-xcframework-debug # └── pkg @@ -119,36 +119,36 @@ gethosttype() # xcframework() { - if [[ ! $OSNAME = *"darwin"* ]]; then - echo "Can only build this on a Mac" - exit 0 - fi - BUILD_TYPE=${1:-release} - UPPERCASE_BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" + if [[ ! $OSNAME = *"darwin"* ]]; then + echo "Can only build this on a Mac" + exit 0 + fi + BUILD_TYPE=${1:-release} + UPPERCASE_BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" - # Build all frameworks - macos-framework $BUILD_TYPE - iphoneos-framework $BUILD_TYPE - iphonesimulator-framework $BUILD_TYPE + # Build all frameworks + macos-framework $BUILD_TYPE + iphoneos-framework $BUILD_TYPE + iphonesimulator-framework $BUILD_TYPE - ARTIFACT="xcframework" - TARGET_PLATFORM="apple" - TARGET_BUILD_DIR=$BUILD_OUTPUT_DIR/$TARGET_PLATFORM-$ARTIFACT-$BUILD_TYPE - rm -rf $TARGET_BUILD_DIR - PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg - mkdir -p $PKG_OUTPUT_DIR + ARTIFACT="xcframework" + TARGET_PLATFORM="apple" + TARGET_BUILD_DIR=$BUILD_OUTPUT_DIR/$TARGET_PLATFORM-$ARTIFACT-$BUILD_TYPE + rm -rf $TARGET_BUILD_DIR + PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg + mkdir -p $PKG_OUTPUT_DIR - MACOS_FRAMEWORK_DIR=macos-x64-framework-$BUILD_TYPE - IOS_FRAMEWORK_DIR=iphoneos-arm64-framework-$BUILD_TYPE - IOS_SIM_FRAMEWORK_DIR=iphonesimulator-x64-framework-$BUILD_TYPE + MACOS_FRAMEWORK_DIR=macos-x64-framework-$BUILD_TYPE + IOS_FRAMEWORK_DIR=iphoneos-arm64-framework-$BUILD_TYPE + IOS_SIM_FRAMEWORK_DIR=iphonesimulator-x64-framework-$BUILD_TYPE - # Pack everything - rm -rf $PKG_OUTPUT_DIR/zt.xcframework # Remove prior to move to prevent error - xcodebuild -create-xcframework \ - -framework $BUILD_CACHE_DIR/$MACOS_FRAMEWORK_DIR/lib/$UPPERCASE_BUILD_TYPE/zt.framework \ - -framework $BUILD_CACHE_DIR/$IOS_FRAMEWORK_DIR/lib/$UPPERCASE_BUILD_TYPE/zt.framework \ - -framework $BUILD_CACHE_DIR/$IOS_SIM_FRAMEWORK_DIR/lib/$UPPERCASE_BUILD_TYPE/zt.framework \ - -output $PKG_OUTPUT_DIR/zt.xcframework + # Pack everything + rm -rf $PKG_OUTPUT_DIR/zt.xcframework # Remove prior to move to prevent error + xcodebuild -create-xcframework \ + -framework $BUILD_CACHE_DIR/$MACOS_FRAMEWORK_DIR/lib/$UPPERCASE_BUILD_TYPE/zt.framework \ + -framework $BUILD_CACHE_DIR/$IOS_FRAMEWORK_DIR/lib/$UPPERCASE_BUILD_TYPE/zt.framework \ + -framework $BUILD_CACHE_DIR/$IOS_SIM_FRAMEWORK_DIR/lib/$UPPERCASE_BUILD_TYPE/zt.framework \ + -output $PKG_OUTPUT_DIR/zt.xcframework } # Build iOS framework @@ -157,8 +157,8 @@ xcframework() # # Example output: # -# - Cache : libzt/cache/iphonesimulator-x64-framework-debug -# - Build output : libzt/dist +# - Cache : libzt/cache/iphonesimulator-x64-framework-debug +# - Build output : libzt/dist # # libzt/dist/iphonesimulator-x64-framework-debug # └── pkg @@ -172,32 +172,32 @@ xcframework() # iphonesimulator-framework() { - if [[ ! $OSNAME = *"darwin"* ]]; then - echo "Can only build this on a Mac" - exit 0 - fi - ARTIFACT="framework" - BUILD_TYPE=${1:-Release} - UPPERCASE_BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" - VARIANT="-DBUILD_IOS_FRAMEWORK=True" - TARGET_PLATFORM="iphonesimulator" - TARGET_MACHINE_TYPE="x64" # presumably - CACHE_DIR=$BUILD_CACHE_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE - TARGET_BUILD_DIR=$BUILD_OUTPUT_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE - rm -rf $TARGET_BUILD_DIR - PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg - mkdir -p $PKG_OUTPUT_DIR - # Generate project - mkdir -p $CACHE_DIR - cd $CACHE_DIR - # iOS (SDK 11+, 64-bit only, arm64) - $CMAKE -G Xcode ../../ $VARIANT - # Build framework - xcodebuild -target zt -configuration "$UPPERCASE_BUILD_TYPE" -sdk "iphonesimulator" - cd - - cp -rf $CACHE_DIR/lib/$UPPERCASE_BUILD_TYPE/*.framework $PKG_OUTPUT_DIR - echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" - $TREE $TARGET_BUILD_DIR + if [[ ! $OSNAME = *"darwin"* ]]; then + echo "Can only build this on a Mac" + exit 0 + fi + ARTIFACT="framework" + BUILD_TYPE=${1:-Release} + UPPERCASE_BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" + VARIANT="-DBUILD_IOS_FRAMEWORK=True" + TARGET_PLATFORM="iphonesimulator" + TARGET_MACHINE_TYPE="x64" # presumably + CACHE_DIR=$BUILD_CACHE_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE + TARGET_BUILD_DIR=$BUILD_OUTPUT_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE + rm -rf $TARGET_BUILD_DIR + PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg + mkdir -p $PKG_OUTPUT_DIR + # Generate project + mkdir -p $CACHE_DIR + cd $CACHE_DIR + # iOS (SDK 11+, 64-bit only, arm64) + $CMAKE -G Xcode ../../ $VARIANT + # Build framework + xcodebuild -target zt -configuration "$UPPERCASE_BUILD_TYPE" -sdk "iphonesimulator" + cd - + cp -rf $CACHE_DIR/lib/$UPPERCASE_BUILD_TYPE/*.framework $PKG_OUTPUT_DIR + echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" + $TREE $TARGET_BUILD_DIR } # Build macOS framework @@ -206,8 +206,8 @@ iphonesimulator-framework() # # Example output: # -# - Cache : libzt/cache/macos-x64-framework-debug -# - Build output : libzt/dist +# - Cache : libzt/cache/macos-x64-framework-debug +# - Build output : libzt/dist # # libzt/dist/macos-x64-framework-debug # └── pkg @@ -221,29 +221,29 @@ iphonesimulator-framework() # macos-framework() { - if [[ ! $OSNAME = *"darwin"* ]]; then - echo "Can only build this on a Mac" - exit 0 - fi - ARTIFACT="framework" - BUILD_TYPE=${1:-Release} - UPPERCASE_BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" - VARIANT="-DBUILD_MACOS_FRAMEWORK=True" - CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE - TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE - rm -rf $TARGET_BUILD_DIR - PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg - mkdir -p $PKG_OUTPUT_DIR - # Generate project - mkdir -p $CACHE_DIR - cd $CACHE_DIR - $CMAKE -G Xcode ../../ $VARIANT - # Build framework - xcodebuild -target zt -configuration $UPPERCASE_BUILD_TYPE -sdk "macosx" - cd - - cp -rf $CACHE_DIR/lib/$UPPERCASE_BUILD_TYPE/*.framework $PKG_OUTPUT_DIR - echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" - $TREE $TARGET_BUILD_DIR + if [[ ! $OSNAME = *"darwin"* ]]; then + echo "Can only build this on a Mac" + exit 0 + fi + ARTIFACT="framework" + BUILD_TYPE=${1:-Release} + UPPERCASE_BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" + VARIANT="-DBUILD_MACOS_FRAMEWORK=True" + CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE + TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE + rm -rf $TARGET_BUILD_DIR + PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg + mkdir -p $PKG_OUTPUT_DIR + # Generate project + mkdir -p $CACHE_DIR + cd $CACHE_DIR + $CMAKE -G Xcode ../../ $VARIANT + # Build framework + xcodebuild -target zt -configuration $UPPERCASE_BUILD_TYPE -sdk "macosx" + cd - + cp -rf $CACHE_DIR/lib/$UPPERCASE_BUILD_TYPE/*.framework $PKG_OUTPUT_DIR + echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" + $TREE $TARGET_BUILD_DIR } # Build iOS framework @@ -252,8 +252,8 @@ macos-framework() # # Example output: # -# - Cache : libzt/cache/iphoneos-arm64-framework-debug -# - Build output : libzt/dist +# - Cache : libzt/cache/iphoneos-arm64-framework-debug +# - Build output : libzt/dist # # libzt/dist/iphoneos-arm64-framework-debug # └── pkg @@ -267,33 +267,33 @@ macos-framework() # iphoneos-framework() { - if [[ ! $OSNAME = *"darwin"* ]]; then - echo "Can only build this on a Mac" - exit 0 - fi - ARTIFACT="framework" - BUILD_TYPE=${1:-Release} - UPPERCASE_BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" - VARIANT="-DBUILD_IOS_FRAMEWORK=True -DIOS_ARM64=True" - TARGET_PLATFORM="iphoneos" - TARGET_MACHINE_TYPE=arm64 - CACHE_DIR=$BUILD_CACHE_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE - TARGET_BUILD_DIR=$BUILD_OUTPUT_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE - rm -rf $TARGET_BUILD_DIR - PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg - mkdir -p $PKG_OUTPUT_DIR - # Generate project - mkdir -p $CACHE_DIR - cd $CACHE_DIR - # iOS (SDK 11+, 64-bit only, arm64) - $CMAKE -G Xcode ../../ $VARIANT - sed -i '' 's/x86_64/$(CURRENT_ARCH)/g' zt.xcodeproj/project.pbxproj - # Build framework - xcodebuild -arch $TARGET_MACHINE_TYPE -target zt -configuration "$UPPERCASE_BUILD_TYPE" -sdk "iphoneos" - cd - - cp -rvf $CACHE_DIR/lib/$UPPERCASE_BUILD_TYPE/*.framework $PKG_OUTPUT_DIR - echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" - $TREE $TARGET_BUILD_DIR + if [[ ! $OSNAME = *"darwin"* ]]; then + echo "Can only build this on a Mac" + exit 0 + fi + ARTIFACT="framework" + BUILD_TYPE=${1:-Release} + UPPERCASE_BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" + VARIANT="-DBUILD_IOS_FRAMEWORK=True -DIOS_ARM64=True" + TARGET_PLATFORM="iphoneos" + TARGET_MACHINE_TYPE=arm64 + CACHE_DIR=$BUILD_CACHE_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE + TARGET_BUILD_DIR=$BUILD_OUTPUT_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE + rm -rf $TARGET_BUILD_DIR + PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg + mkdir -p $PKG_OUTPUT_DIR + # Generate project + mkdir -p $CACHE_DIR + cd $CACHE_DIR + # iOS (SDK 11+, 64-bit only, arm64) + $CMAKE -G Xcode ../../ $VARIANT + sed -i '' 's/x86_64/$(CURRENT_ARCH)/g' zt.xcodeproj/project.pbxproj + # Build framework + xcodebuild -arch $TARGET_MACHINE_TYPE -target zt -configuration "$UPPERCASE_BUILD_TYPE" -sdk "iphoneos" + cd - + cp -rvf $CACHE_DIR/lib/$UPPERCASE_BUILD_TYPE/*.framework $PKG_OUTPUT_DIR + echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" + $TREE $TARGET_BUILD_DIR } # Build standard libraries, examples, and selftest @@ -302,8 +302,8 @@ iphoneos-framework() # # Example output: # -# - Cache : libzt/cache/linux-x64-host-release -# - Build output : libzt/dist +# - Cache : libzt/cache/linux-x64-host-release +# - Build output : libzt/dist # # linux-x64-host-release # ├── bin @@ -315,35 +315,35 @@ iphoneos-framework() # host() { - ARTIFACT="host" - # Default to release - BUILD_TYPE=${1:-release} - VARIANT="-DBUILD_HOST=True" - CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE - TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE - rm -rf $TARGET_BUILD_DIR - LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib - BIN_OUTPUT_DIR=$TARGET_BUILD_DIR/bin - mkdir -p $LIB_OUTPUT_DIR - mkdir -p $BIN_OUTPUT_DIR - $CMAKE $VARIANT -H. -B$CACHE_DIR -DCMAKE_BUILD_TYPE=$BUILD_TYPE - $CMAKE --build $CACHE_DIR $BUILD_CONCURRENCY - cp -f $CACHE_DIR/lib/libzt.* $LIB_OUTPUT_DIR - cp -f $CACHE_DIR/bin/* $BIN_OUTPUT_DIR - echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" - $TREE $TARGET_BUILD_DIR + ARTIFACT="host" + # Default to release + BUILD_TYPE=${1:-release} + VARIANT="-DBUILD_HOST=True" + CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE + TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE + rm -rf $TARGET_BUILD_DIR + LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib + BIN_OUTPUT_DIR=$TARGET_BUILD_DIR/bin + mkdir -p $LIB_OUTPUT_DIR + mkdir -p $BIN_OUTPUT_DIR + $CMAKE $VARIANT -H. -B$CACHE_DIR -DCMAKE_BUILD_TYPE=$BUILD_TYPE + $CMAKE --build $CACHE_DIR $BUILD_CONCURRENCY + cp -f $CACHE_DIR/lib/libzt.* $LIB_OUTPUT_DIR + cp -f $CACHE_DIR/bin/* $BIN_OUTPUT_DIR + echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" + $TREE $TARGET_BUILD_DIR } host-install() { - cd cache/$HOST_PLATFORM-$HOST_MACHINE_TYPE-host-$1/ - make install - cd - + cd cache/$HOST_PLATFORM-$HOST_MACHINE_TYPE-host-$1/ + make install + cd - } host-uninstall() { - cd cache/$HOST_PLATFORM-$HOST_MACHINE_TYPE-host-$1/ - xargs rm < install_manifest.txt - cd - + cd cache/$HOST_PLATFORM-$HOST_MACHINE_TYPE-host-$1/ + xargs rm < install_manifest.txt + cd - } # Build C extension module (*.so), python module, package both into wheel @@ -358,98 +358,117 @@ host-uninstall() # host-python-wheel() { - ARTIFACT="python" - # Default to release - BUILD_TYPE=${1:-release} - CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE - TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE - PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg - mkdir -p $PKG_OUTPUT_DIR - # Requires setuptools, etc - cd pkg/pypi && ./build.sh wheel && cp -f dist/*.whl $PKG_OUTPUT_DIR + ARTIFACT="python" + # Default to release + BUILD_TYPE=${1:-release} + CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE + TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE + PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg + mkdir -p $PKG_OUTPUT_DIR + # Requires setuptools, etc + cd pkg/pypi && ./build.sh wheel && cp -f dist/*.whl $PKG_OUTPUT_DIR } # Build shared library with python wrapper symbols exported host-python() { - ARTIFACT="python" - # Default to release - BUILD_TYPE=${1:-release} - VARIANT="-DZTS_ENABLE_PYTHON=True" - CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE - TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE - LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib - rm -rf $LIB_OUTPUT_DIR - mkdir -p $LIB_OUTPUT_DIR - # Optional step to generate new SWIG wrapper - swig -c++ -python -o src/bindings/python/zt_wrap.cxx -Iinclude src/bindings/python/zt.i - $CMAKE $VARIANT -H. -B$CACHE_DIR -DCMAKE_BUILD_TYPE=$BUILD_TYPE - $CMAKE --build $CACHE_DIR $BUILD_CONCURRENCY - cp -f $CACHE_DIR/lib/$SHARED_LIB_NAME $LIB_OUTPUT_DIR/_libzt.so - echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" - $TREE $TARGET_BUILD_DIR + ARTIFACT="python" + # Default to release + BUILD_TYPE=${1:-release} + VARIANT="-DZTS_ENABLE_PYTHON=True" + CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE + TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE + LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib + rm -rf $LIB_OUTPUT_DIR + mkdir -p $LIB_OUTPUT_DIR + # Optional step to generate new SWIG wrapper + swig -c++ -python -o src/bindings/python/zt_wrap.cxx -Iinclude src/bindings/python/zt.i + $CMAKE $VARIANT -H. -B$CACHE_DIR -DCMAKE_BUILD_TYPE=$BUILD_TYPE + $CMAKE --build $CACHE_DIR $BUILD_CONCURRENCY + cp -f $CACHE_DIR/lib/$SHARED_LIB_NAME $LIB_OUTPUT_DIR/_libzt.so + echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" + $TREE $TARGET_BUILD_DIR } # Build shared library with P/INVOKE wrapper symbols exported host-pinvoke() { - ARTIFACT="pinvoke" - # Default to release - BUILD_TYPE=${1:-release} - VARIANT="-DZTS_ENABLE_PINVOKE=True" - CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE - TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE - rm -rf $TARGET_BUILD_DIR - LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib - BIN_OUTPUT_DIR=$TARGET_BUILD_DIR/bin - mkdir -p $LIB_OUTPUT_DIR - $CMAKE $VARIANT -H. -B$CACHE_DIR -DCMAKE_BUILD_TYPE=$BUILD_TYPE - $CMAKE --build $CACHE_DIR $BUILD_CONCURRENCY - cp -f $CACHE_DIR/lib/libzt.* $LIB_OUTPUT_DIR - echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" - $TREE $TARGET_BUILD_DIR + ARTIFACT="pinvoke" + # Default to release + BUILD_TYPE=${1:-release} + VARIANT="-DZTS_ENABLE_PINVOKE=True" + CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE + TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE + rm -rf $TARGET_BUILD_DIR + LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib + BIN_OUTPUT_DIR=$TARGET_BUILD_DIR/bin + mkdir -p $LIB_OUTPUT_DIR + $CMAKE $VARIANT -H. -B$CACHE_DIR -DCMAKE_BUILD_TYPE=$BUILD_TYPE + $CMAKE --build $CACHE_DIR $BUILD_CONCURRENCY + cp -f $CACHE_DIR/lib/libzt.* $LIB_OUTPUT_DIR + echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" + $TREE $TARGET_BUILD_DIR } # Build shared library with Java JNI wrapper symbols exported (.jar) host-jar() { - ARTIFACT="jar" - # Default to release - BUILD_TYPE=${1:-release} - if [[ $1 = *"docs"* ]]; then - # Generate documentation - javadoc src/bindings/java/*.java -d docs/java - exit 0 - fi - VARIANT="-DZTS_ENABLE_JAVA=True" - CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE - TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE - rm -rf $TARGET_BUILD_DIR - PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg - mkdir -p $PKG_OUTPUT_DIR - # Share same cache dir with CMake - JAVA_JAR_DIR=$CACHE_DIR/pkg/jar - JAVA_JAR_SOURCE_TREE_DIR=$JAVA_JAR_DIR/com/zerotier/libzt/ - mkdir -p $JAVA_JAR_SOURCE_TREE_DIR - cp -f src/bindings/java/*.java $JAVA_JAR_SOURCE_TREE_DIR - # Build - $CMAKE $VARIANT -H. -B$CACHE_DIR -DCMAKE_BUILD_TYPE=$BUILD_TYPE - $CMAKE --build $CACHE_DIR $BUILD_CONCURRENCY - # Package everything - cp -f $CACHE_DIR/lib/libzt.* $JAVA_JAR_DIR - cd $JAVA_JAR_DIR - export JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8 - javac -Xlint:deprecation com/zerotier/libzt/*.java - jar cf libzt-"$(git describe --abbrev=0)".jar $SHARED_LIB_NAME com/zerotier/libzt/*.class - rm -rf com $SHARED_LIB_NAME - cd - - # Copy JAR to dist/ - echo -e "\nContents of JAR:\n" - jar tf $JAVA_JAR_DIR/*.jar - echo -e - mv $JAVA_JAR_DIR/*.jar $PKG_OUTPUT_DIR - echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" - $TREE $TARGET_BUILD_DIR + ARTIFACT="jar" + # Default to release + BUILD_TYPE=${1:-release} + if [[ $1 = *"docs"* ]]; then + # Generate documentation + javadoc src/bindings/java/*.java -d docs/java + exit 0 + fi + VARIANT="-DZTS_ENABLE_JAVA=True" + CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE + TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE + rm -rf $TARGET_BUILD_DIR + PKG_OUTPUT_DIR=$TARGET_BUILD_DIR/pkg + mkdir -p $PKG_OUTPUT_DIR + # Share same cache dir with CMake + JAVA_JAR_DIR=$CACHE_DIR/pkg/jar + JAVA_JAR_SOURCE_TREE_DIR=$JAVA_JAR_DIR/com/zerotier/sdk/ + mkdir -p $JAVA_JAR_SOURCE_TREE_DIR + cp -f src/bindings/java/*.java $JAVA_JAR_SOURCE_TREE_DIR + # Build + $CMAKE $VARIANT -H. -B$CACHE_DIR -DCMAKE_BUILD_TYPE=$BUILD_TYPE + $CMAKE --build $CACHE_DIR $BUILD_CONCURRENCY + # Package everything + cp -f $CACHE_DIR/lib/libzt.* $JAVA_JAR_DIR + cd $JAVA_JAR_DIR + export JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8 + javac -Xlint:deprecation com/zerotier/sdk/*.java + jar cf libzt-"$(git describe --abbrev=0)".jar $SHARED_LIB_NAME com/zerotier/sdk/*.class + rm -rf com $SHARED_LIB_NAME + cd - + # Copy JAR to dist/ + echo -e "\nContents of JAR:\n" + jar tf $JAVA_JAR_DIR/*.jar + echo -e + mv $JAVA_JAR_DIR/*.jar $PKG_OUTPUT_DIR + echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" + $TREE $TARGET_BUILD_DIR + + # Test JAR + if [[ $2 = *"test"* ]]; then + if [[ -z "${alice_path}" ]]; then + echo "Please set necessary environment variables for test" + exit 0 + fi + cd test + rm -rf *.dylib + rm -rf *.jar + cp -f $PKG_OUTPUT_DIR/*.jar . + # Unpack JAR to get access to shared library + jar xf *.jar libzt.dylib + javac -cp *.jar selftest.java + # Start Alice as server + java -cp ".:libzt-"$(git describe --abbrev=0)".jar" selftest server $alice_path $testnet $port4 & + # Start Bob as client + java -cp ".:libzt-"$(git describe --abbrev=0)".jar" selftest client $bob_path $testnet $alice_ip4 $port4 & + fi } @@ -463,13 +482,12 @@ ANDROID_PKG_PROJ_DIR=$(pwd)/pkg/android #export PATH=/Library/Java/JavaVirtualMachines/$JDK/Contents/Home/bin/:${PATH} #export PATH=/Users/$USER/Library/Android/sdk/platform-tools/:${PATH} GRADLE_ARGS=--stacktrace -#ANDROID_APP_NAME=com.example.mynewestapplication # for our purposes we limit this to execution on macOS if [[ $OSNAME = *"linux"* ]]; then - export ANDROID_HOME=/usr/lib/android-sdk/ + export ANDROID_HOME=/usr/lib/android-sdk/ fi if [[ $OSNAME = *"darwin"* ]]; then - export ANDROID_HOME=/Users/$USER/Library/Android/sdk + export ANDROID_HOME=/Users/$USER/Library/Android/sdk fi # Build shared library with Java JNI wrapper symbols exported (.aar) @@ -478,179 +496,182 @@ fi # # Example output: # -# - Cache : libzt/cache/android-any-android-release -# - Build output : libzt/dist +# - Cache : libzt/cache/android-any-android-release +# - Build output : libzt/dist # # android-any-android-release # └── libzt-release.aar # android-aar() { - ARTIFACT="android" - BUILD_TYPE=${1:-release} # Default to release - CMAKE_SWITCH="ZTS_ENABLE_JAVA" - TARGET_PLATFORM="android" - TARGET_MACHINE_TYPE=any - CACHE_DIR=$BUILD_CACHE_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE - PKG_OUTPUT_DIR=$BUILD_OUTPUT_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE - mkdir -p $CACHE_DIR - mkdir -p $PKG_OUTPUT_DIR - # Unsure why, but Gradle's build script chokes on this non-source file now - rm -rf ext/ZeroTierOne/ext/miniupnpc/VERSION - export PATH=$ANDROID_HOME/cmdline-tools/tools/bin:$PATH - # Copy source files into project - cp -f src/bindings/java/*.java ${ANDROID_PKG_PROJ_DIR}/app/src/main/java/com/zerotier/libzt - # Build - UPPERCASE_BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" - CMAKE_FLAGS="-D${CMAKE_SWITCH}=1 -D${CMAKE_SWITCH}=ON" - cd $ANDROID_PKG_PROJ_DIR - ./gradlew $GRADLE_ARGS assemble$UPPERCASE_BUILD_TYPE # assembleRelease / assembleDebug - mv $ANDROID_PKG_PROJ_DIR/app/build/outputs/aar/*.aar \ - $PKG_OUTPUT_DIR/libzt-$BUILD_TYPE.aar - cd - - echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" - $TREE $PKG_OUTPUT_DIR + ARTIFACT="android" + BUILD_TYPE=${1:-release} # Default to release + CMAKE_SWITCH="ZTS_ENABLE_JAVA" + TARGET_PLATFORM="android" + TARGET_MACHINE_TYPE=any + CACHE_DIR=$BUILD_CACHE_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE + PKG_OUTPUT_DIR=$BUILD_OUTPUT_DIR/$TARGET_PLATFORM-$TARGET_MACHINE_TYPE-$ARTIFACT-$BUILD_TYPE + mkdir -p $CACHE_DIR + mkdir -p $PKG_OUTPUT_DIR + # Unsure why, but Gradle's build script chokes on this non-source file now + rm -rf ext/ZeroTierOne/ext/miniupnpc/VERSION + export PATH=$ANDROID_HOME/cmdline-tools/tools/bin:$PATH + # Copy source files into project + cp -f src/bindings/java/*.java ${ANDROID_PKG_PROJ_DIR}/app/src/main/java/com/zerotier/libzt + # Build + UPPERCASE_BUILD_TYPE="$(tr '[:lower:]' '[:upper:]' <<< ${BUILD_TYPE:0:1})${BUILD_TYPE:1}" + CMAKE_FLAGS="-D${CMAKE_SWITCH}=1 -D${CMAKE_SWITCH}=ON" + cd $ANDROID_PKG_PROJ_DIR + ./gradlew $GRADLE_ARGS assemble$UPPERCASE_BUILD_TYPE # assembleRelease / assembleDebug + mv $ANDROID_PKG_PROJ_DIR/app/build/outputs/aar/*.aar \ + $PKG_OUTPUT_DIR/libzt-$BUILD_TYPE.aar + cd - + echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" + $TREE $PKG_OUTPUT_DIR } # Build static library and selftest. Currently this only tests # the core C API, not any of the language bindings. test() { - ARTIFACT="test" - # Default to release - BUILD_TYPE=${1:-release} - VARIANT="-DBUILD_HOST_SELFTEST_ONLY=True" - CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE - TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE - rm -rf $TARGET_BUILD_DIR - LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib - BIN_OUTPUT_DIR=$TARGET_BUILD_DIR/bin - mkdir -p $BIN_OUTPUT_DIR - $CMAKE $VARIANT -H. -B$CACHE_DIR -DCMAKE_BUILD_TYPE=$BUILD_TYPE - $CMAKE --build $CACHE_DIR $BUILD_CONCURRENCY - cp -f $CACHE_DIR/bin/* $BIN_OUTPUT_DIR - echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" - $TREE $TARGET_BUILD_DIR - # Test - cd $CACHE_DIR - #ctest -C release - cd - + ARTIFACT="test" + # Default to release + BUILD_TYPE=${1:-release} + VARIANT="-DBUILD_HOST_SELFTEST_ONLY=True" + CACHE_DIR=$DEFAULT_HOST_BUILD_CACHE_DIR-$ARTIFACT-$BUILD_TYPE + TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE + rm -rf $TARGET_BUILD_DIR + LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib + BIN_OUTPUT_DIR=$TARGET_BUILD_DIR/bin + mkdir -p $BIN_OUTPUT_DIR + $CMAKE $VARIANT -H. -B$CACHE_DIR -DCMAKE_BUILD_TYPE=$BUILD_TYPE + $CMAKE --build $CACHE_DIR $BUILD_CONCURRENCY + cp -f $CACHE_DIR/bin/* $BIN_OUTPUT_DIR + echo -e "\n - Build cache : $CACHE_DIR\n - Build output : $BUILD_OUTPUT_DIR\n" + $TREE $TARGET_BUILD_DIR + # Test + cd $CACHE_DIR + #ctest -C release + cd - } format-code() { - if [[ ! $(which clang-format) = "" ]]; - then - # Eventually: find . -path ./ext -prune -false -o -type f \( -iname \*.c -o -iname \*.h -o -iname \*.cpp -o -iname \*.hpp \) -exec clang-format -i {} \; - clang-format -i include/*.h \ - src/*.c \ - src/*.cpp \ - src/*.hpp \ - examples/c/*.c \ - test/*.c \ - src/bindings/csharp/*.cs \ - src/bindings/csharp/*.cxx \ - examples/csharp/*.cs - return 0 - else - echo "Please install clang-format." - fi + if [[ ! $(which clang-format) = "" ]]; + then + # Eventually: find . -path ./ext -prune -false -o -type f \( -iname \*.c -o -iname \*.h -o -iname \*.cpp -o -iname \*.hpp \) -exec clang-format -i {} \; + clang-format -i include/*.h \ + src/*.c \ + src/*.cpp \ + src/*.hpp \ + examples/c/*.c \ + examples/csharp/*.cs \ + examples/java/*.java \ + test/*.c \ + test/*.cs \ + src/bindings/csharp/*.cs \ + src/bindings/csharp/*.cxx \ + src/bindings/java/*.java \ + src/bindings/java/*.cxx \ + examples/csharp/*.cs + return 0 + else + echo "Please install clang-format." + fi } # Test C API test-c() { - if [[ -z "${alice_path}" ]]; then - echo "Please set necessary environment variables for test" - #exit 0 - fi - BUILD_TYPE=${1:-debug} - test $BUILD_TYPE - # Build selftest - #test $BUILD_TYPE - # Clear logs - rm -rf test/*.txt - # Start Alice as server - echo "$BIN_OUTPUT_DIR/selftest-c" $alice_path $testnet $port4 $port6 & - # Start Bob as client - echo "$BIN_OUTPUT_DIR/selftest-c" $bob_path $testnet $port4 $alice_ip4 $port6 $alice_ip6 & + if [[ -z "${alice_path}" ]]; then + echo "Please set necessary environment variables for test" + #exit 0 + fi + BUILD_TYPE=${1:-debug} + #BUILD_TYPE="ASAN" + # Build selftest + test $BUILD_TYPE + # Start Alice as server + "$BIN_OUTPUT_DIR/selftest-c" $alice_path $testnet $port4 $port6 & + # Start Bob as client + "$BIN_OUTPUT_DIR/selftest-c" $bob_path $testnet $port4 $alice_ip4 $port6 $alice_ip6 & } # Test C# API test-cs() { - if [[ -z "${alice_path}" ]]; then - echo "Please set necessary environment variables for test" - exit 0 - fi - ARTIFACT="pinvoke" - # Default to debug so asserts aren't optimized out - BUILD_TYPE=${1:-debug} - TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE - LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib - BIN_OUTPUT_DIR=$TARGET_BUILD_DIR/bin - rm -rf $TARGET_BUILD_DIR - # Build C shared library exporting C# symbols - host-pinvoke $1 - # TODO: This should eventually be converted to a proper dotnet project - # Build C# managed API library - csc -target:library -doc:$LIB_OUTPUT_DIR/ZeroTier.Sockets.xml -out:$LIB_OUTPUT_DIR/ZeroTier.Sockets.dll src/bindings/csharp/*.cs - # Build selftest - mkdir -p $BIN_OUTPUT_DIR - csc -out:$BIN_OUTPUT_DIR/selftest.exe -reference:$LIB_OUTPUT_DIR/ZeroTier.Sockets.dll test/selftest.cs - # Copy shared libraries into bin directory so they can be discovered by dlopen - cp $LIB_OUTPUT_DIR/* $BIN_OUTPUT_DIR - # Start Alice as server - mono --debug "$BIN_OUTPUT_DIR/selftest.exe" $alice_path $testnet $port4 $port6 & - sleep 10 - # Start Bob as client - mono --debug "$BIN_OUTPUT_DIR/selftest.exe" $bob_path $testnet $port4 $alice_ip4 $port6 $alice_ip6 & + if [[ -z "${alice_path}" ]]; then + echo "Please set necessary environment variables for test" + exit 0 + fi + ARTIFACT="pinvoke" + # Default to debug so asserts aren't optimized out + BUILD_TYPE=${1:-debug} + TARGET_BUILD_DIR=$DEFAULT_HOST_BIN_OUTPUT_DIR-$ARTIFACT-$BUILD_TYPE + LIB_OUTPUT_DIR=$TARGET_BUILD_DIR/lib + BIN_OUTPUT_DIR=$TARGET_BUILD_DIR/bin + rm -rf $TARGET_BUILD_DIR + # Build C shared library exporting C# symbols + host-pinvoke $1 + # TODO: This should eventually be converted to a proper dotnet project + # Build C# managed API library + csc -target:library -doc:$LIB_OUTPUT_DIR/ZeroTier.Sockets.xml -out:$LIB_OUTPUT_DIR/ZeroTier.Sockets.dll src/bindings/csharp/*.cs + # Build selftest + mkdir -p $BIN_OUTPUT_DIR + csc -out:$BIN_OUTPUT_DIR/selftest.exe -reference:$LIB_OUTPUT_DIR/ZeroTier.Sockets.dll test/selftest.cs + # Copy shared libraries into bin directory so they can be discovered by dlopen + cp $LIB_OUTPUT_DIR/* $BIN_OUTPUT_DIR + # Start Alice as server + mono --debug "$BIN_OUTPUT_DIR/selftest.exe" $alice_path $testnet $port4 $port6 & + sleep 10 + # Start Bob as client + mono --debug "$BIN_OUTPUT_DIR/selftest.exe" $bob_path $testnet $port4 $alice_ip4 $port6 $alice_ip6 & } # Recursive deep clean clean() { - # Finished artifacts - rm -rf $BUILD_OUTPUT_DIR - # CMake's build system cache - rm -rf $BUILD_CACHE_DIR - # CMake test output - rm -rf bin - rm -rf Testing - rm -rf CMakeFiles - rm -rf *.cmake - rm -rf CMakeCache.txt - rm -rf Makefile - # Android AAR project binaries and sources (copied from src/bindings/java) - rm -rf $ANDROID_PKG_PROJ_DIR/app/build - rm -rf $ANDROID_PKG_PROJ_DIR/app/src/main/java/com/zerotier/libzt/*.java - rm -rf $ANDROID_PKG_PROJ_DIR/app/.externalNativeBuild - # Remove whatever remains - find . \ - \( -name '*.dylib' \ - -o -name '*.dll' \ - -o -name '*.aar' \ - -o -name '*.jar' \ - -o -name '*.so' \ - -o -name '*.a' \ - -o -name '*.o' \ - -o -name '*.exe' \ - -o -name '*.o.d' \ - -o -name '*.out' \ - -o -name '*.log' \ - -o -name '*.dSYM' \ - -o -name '*.class' \ - \) -exec rm -rf {} + + # Finished artifacts + rm -rf $BUILD_OUTPUT_DIR + # CMake's build system cache + rm -rf $BUILD_CACHE_DIR + # CMake test output + rm -rf bin + rm -rf Testing + rm -rf CMakeFiles + rm -rf *.cmake + rm -rf CMakeCache.txt + rm -rf Makefile + # Android AAR project binaries and sources (copied from src/bindings/java) + rm -rf $ANDROID_PKG_PROJ_DIR/app/build + rm -rf $ANDROID_PKG_PROJ_DIR/app/src/main/java/com/zerotier/libzt/*.java + rm -rf $ANDROID_PKG_PROJ_DIR/app/.externalNativeBuild + # Remove whatever remains + find . \ + \( -name '*.dylib' \ + -o -name '*.dll' \ + -o -name '*.aar' \ + -o -name '*.jar' \ + -o -name '*.so' \ + -o -name '*.a' \ + -o -name '*.o' \ + -o -name '*.exe' \ + -o -name '*.o.d' \ + -o -name '*.out' \ + -o -name '*.log' \ + -o -name '*.dSYM' \ + -o -name '*.class' \ + \) -exec rm -rf {} + - find . -type d -name "__pycache__" -exec rm -rf {} + + find . -type d -name "__pycache__" -exec rm -rf {} + } list() { - IFS=$'\n' - for f in $(declare -F); do - echo "${f:11}" - done + IFS=$'\n' + for f in $(declare -F); do + echo "${f:11}" + done } "$@" diff --git a/examples/java/Example.java b/examples/java/Example.java index a5f23de..ba3d761 100644 --- a/examples/java/Example.java +++ b/examples/java/Example.java @@ -1,161 +1,138 @@ -//package com.zerotier.libzt.javasimpleexample; - -import com.zerotier.libzt.ZeroTier; -import com.zerotier.libzt.ZeroTierEventListener; +import com.zerotier.sdk.*; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.math.BigInteger; -public class Example { +public class selftest { + public static void main(String[] args) + { + System.err.println(args.length); + if (args.length < 4 || args.length > 5) { + System.err.println("Invalid arguments"); + System.err.println(" Usage: "); + System.err.println(" example server ./ 0123456789abcdef 8080"); + System.err.println(" example client ./ 0123456789abcdef 192.168.22.1 8080\n"); + System.exit(1); + } - static void sleep(int ms) { - try { - Thread.sleep(ms); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + String storagePath = ""; + String remoteAddr = ""; + int port = 0; + String mode = args[0]; + storagePath = args[1]; + BigInteger networkId = new BigInteger(args[2], 16); + if (args.length == 4) { + port = Integer.parseInt(args[3]); + } + if (args.length == 5) { + remoteAddr = args[3]; + port = Integer.parseInt(args[4]); + } + System.out.println("mode = " + mode); + System.out.println("networkId = " + Long.toHexString(networkId.longValue())); + System.out.println("storagePath = " + storagePath); + System.out.println("remoteAddr = " + remoteAddr); + System.out.println("port = " + port); - public static void main(String[] args) { - String keyPath="."; // Default to current directory - BigInteger networkId; - Integer servicePort=0; - if (args.length != 3) { - System.err.println(" Invalid arguments"); - System.err.println(" Usage: example "); - System.exit(1); - } - keyPath = args[0]; - networkId = new BigInteger(args[1], 16); - servicePort = Integer.parseInt(args[2]); - System.out.println("networkId = " + Long.toHexString(networkId.longValue())); - System.out.println("keyPath = " + keyPath); - System.out.println("servicePort = " + servicePort); + // ZeroTier setup - // - // BEGIN SETUP - // + ZeroTierNode node = new ZeroTierNode(); - // Set up event listener and start service - MyZeroTierEventListener listener = new MyZeroTierEventListener(); - ZeroTier.start(keyPath, (ZeroTierEventListener)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(networkId.longValue()); - // Wait for EVENT_NETWORK_READY_IP4/6 - System.out.println("waiting for network config..."); - while (listener.isNetworkReady == false) { sleep(50); } - System.out.println("joined"); + node.initFromStorage(storagePath); + // node.initSetEventHandler(new MyZeroTierEventListener()); + // node.initSetPort(9994); + node.start(); - // - // END SETUP - // + System.out.println("Waiting for node to come online..."); + while (! node.isOnline()) { + ZeroTierNative.zts_util_delay(50); + } + System.out.println("Node ID: " + Long.toHexString(node.getId())); + System.out.println("Joining network..."); + node.join(networkId.longValue()); + System.out.println("Waiting for network..."); + while (! node.isNetworkTransportReady(networkId.longValue())) { + ZeroTierNative.zts_util_delay(50); + } + System.out.println("joined"); - sleep(120000); + // Socket logic - /** - * - * Begin using socket API after this point: - * - * ZeroTier.ZeroTierSocket, ZeroTier.ZeroTierSocketFactory, etc - * - * (or) - * - * ZeroTier.socket(), ZeroTier.connect(), etc - */ - } + if (mode.equals("server")) { + System.out.println("Starting server..."); + try { + ZeroTierSocket socket = + new ZeroTierSocket(ZeroTierNative.ZTS_AF_INET, ZeroTierNative.ZTS_SOCK_STREAM, 0); + socket.bind("0.0.0.0", port); + socket.listen(100); + ZeroTierSocket newConnection = socket.accept(); + ZeroTierInputStream inputStream = newConnection.getInputStream(); + DataInputStream dataInputStream = new DataInputStream(inputStream); + String message = dataInputStream.readUTF(); + System.out.println("recv: " + message); + socket.close(); + newConnection.close(); + } + catch (Exception ex) { + System.out.println(ex); + } + } + + if (mode.equals("client")) { + System.out.println("Starting client..."); + try { + ZeroTierSocket socket = + new ZeroTierSocket(ZeroTierNative.ZTS_AF_INET, ZeroTierNative.ZTS_SOCK_STREAM, 0); + socket.connect(remoteAddr, port); + ZeroTierOutputStream outputStream = socket.getOutputStream(); + DataOutputStream dataOutputStream = new DataOutputStream(outputStream); + dataOutputStream.writeUTF("Hello from java!"); + socket.close(); + } + catch (Exception ex) { + System.out.println(ex); + } + } + } } /** - * Example event handling + * (OPTIONAL) event handler */ -class MyZeroTierEventListener implements ZeroTierEventListener { - - public boolean isNetworkReady = false; - public boolean isOnline = false; - - public void onZeroTierEvent(long id, int eventCode) { - if (eventCode == ZeroTier.EVENT_NODE_UP) { - System.out.println("EVENT_NODE_UP: nodeId=" + Long.toHexString(id)); - } - if (eventCode == ZeroTier.EVENT_NODE_ONLINE) { - // The core service is running properly and can join networks now - System.out.println("EVENT_NODE_ONLINE: nodeId=" + Long.toHexString(id)); - isOnline = true; - } - if (eventCode == ZeroTier.EVENT_NODE_OFFLINE) { - // Network does not seem to be reachable by any available strategy - System.out.println("EVENT_NODE_OFFLINE"); - } - if (eventCode == ZeroTier.EVENT_NODE_DOWN) { - // Called when the node is shutting down - System.out.println("EVENT_NODE_DOWN"); - } - if (eventCode == ZeroTier.EVENT_NODE_IDENTITY_COLLISION) { - // Another node with this identity already exists - System.out.println("EVENT_NODE_IDENTITY_COLLISION"); - } - if (eventCode == ZeroTier.EVENT_NODE_UNRECOVERABLE_ERROR) { - // Try again - System.out.println("EVENT_NODE_UNRECOVERABLE_ERROR"); - } - if (eventCode == ZeroTier.EVENT_NODE_NORMAL_TERMINATION) { - // Normal closure - System.out.println("EVENT_NODE_NORMAL_TERMINATION"); - } - 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)); - isNetworkReady = true; - } - if (eventCode == ZeroTier.EVENT_NETWORK_READY_IP6) { - // We have at least one assigned address and we've received a network configuration - System.out.println("ZTS_EVENT_NETWORK_READY_IP6: nwid=" + Long.toHexString(id)); - //isNetworkReady = true; - } - if (eventCode == ZeroTier.EVENT_NETWORK_DOWN) { - // Someone called leave(), we have no assigned addresses, or otherwise cannot use this interface - System.out.println("EVENT_NETWORK_DOWN: nwid=" + Long.toHexString(id)); - } - if (eventCode == ZeroTier.EVENT_NETWORK_REQ_CONFIG) { - // Waiting for network configuration - System.out.println("EVENT_NETWORK_REQ_CONFIG: nwid=" + Long.toHexString(id)); - } - if (eventCode == ZeroTier.EVENT_NETWORK_OK) { - // Config received and this node is authorized for this network - System.out.println("EVENT_NETWORK_OK: nwid=" + Long.toHexString(id)); - } - if (eventCode == ZeroTier.EVENT_NETWORK_ACCESS_DENIED) { - // You are not authorized to join this network - System.out.println("EVENT_NETWORK_ACCESS_DENIED: nwid=" + Long.toHexString(id)); - } - if (eventCode == ZeroTier.EVENT_NETWORK_NOT_FOUND) { - // The virtual network does not exist - System.out.println("EVENT_NETWORK_NOT_FOUND: nwid=" + Long.toHexString(id)); - } - if (eventCode == ZeroTier.EVENT_NETWORK_CLIENT_TOO_OLD) { - // The core version is too old - System.out.println("EVENT_NETWORK_CLIENT_TOO_OLD: nwid=" + Long.toHexString(id)); - } - if (eventCode == ZeroTier.EVENT_PEER_DIRECT) { - System.out.println("EVENT_PEER_DIRECT: id=" + Long.toHexString(id)); /* - ZeroTierPeerDetails details = new ZeroTierPeerDetails(); - ZeroTier.get_peer(id, details); - System.out.println("address="+Long.toHexString(details.address)); - System.out.println("pathCount="+details.pathCount); - System.out.println("version="+details.versionMajor+"."+details.versionMinor+"."+details.versionRev); - System.out.println("latency="+details.latency); // Not relevant - System.out.println("role="+details.role); // Not relevent - // Print all known paths - for (int i=0; i - -extern int zts_errno; - -namespace ZeroTier { - -extern uint8_t _serviceStateFlags; - -#ifdef __cplusplus -extern "C" { -#endif - -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); - -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 into return value for JNI functions only -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_connect( - JNIEnv *env, jobject thisObj, jint fd, jobject addr) -{ - struct zts_sockaddr_storage ss; - zta2ss(env, &ss, addr); - 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); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_bind( - JNIEnv *env, jobject thisObj, jint fd, jobject addr) -{ - struct zts_sockaddr_storage ss; - zta2ss(env, &ss, addr); - 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); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_listen( - JNIEnv *env, jobject thisObj, jint fd, int backlog) -{ - int retval = zts_listen(fd, backlog); - return retval > -1 ? retval : -(zts_errno); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_accept( - JNIEnv *env, jobject thisObj, jint fd, jobject addr, jint port) -{ - 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); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_setsockopt( - JNIEnv *env, jobject thisObj, jint fd, jint level, jint optname, jobject optval) -{ - jclass c = env->GetObjectClass(optval); - if (!c) { - return ZTS_ERR_SERVICE; - } - int optval_int = -1; - - if (optname == SO_BROADCAST - || optname == SO_KEEPALIVE - || optname == SO_REUSEADDR - || optname == SO_REUSEPORT - || optname == TCP_NODELAY) - { - jfieldID fid = env->GetFieldID(c, "booleanValue", "Z"); - optval_int = (int)(env->GetBooleanField(optval, fid)); - } - if (optname == IP_TTL - || optname == SO_RCVTIMEO - || optname == IP_TOS - || optname == SO_LINGER - || optname == SO_RCVBUF - || optname == SO_SNDBUF) - { - jfieldID fid = env->GetFieldID(c, "integerValue", "I"); - optval_int = env->GetIntField(optval, fid); - } - - int retval = ZTS_ERR_OK; - - if (optname == SO_RCVTIMEO) { - struct timeval tv; - // Convert milliseconds from setSoTimeout() call to seconds and microseconds - tv.tv_usec = optval_int * 1000; - tv.tv_sec = optval_int / 1000000; - retval = zts_setsockopt(fd, level, optname, &tv, sizeof(tv)); - } - else { - retval = zts_setsockopt(fd, level, optname, &optval_int, sizeof(optval_int)); - } - return retval > -1 ? retval : -(zts_errno); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_getsockopt( - JNIEnv *env, jobject thisObj, jint fd, jint level, jint optname, jobject optval) -{ - jclass c = env->GetObjectClass(optval); - if (!c) { - return ZTS_ERR_SERVICE; - } - int optval_int = 0; - zts_socklen_t optlen; // Intentionally not used - - int retval; - - if (optname == SO_RCVTIMEO) { - struct zts_timeval tv; - optlen = sizeof(tv); - retval = zts_getsockopt(fd, level, optname, &tv, &optlen); - // Convert seconds and microseconds back to milliseconds - optval_int = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); - } - else { - retval = zts_getsockopt(fd, level, optname, &optval_int, &optlen); - } - - if (optname == SO_BROADCAST - || optname == SO_KEEPALIVE - || optname == SO_REUSEADDR - || optname == SO_REUSEPORT - || optname == TCP_NODELAY) - { - jfieldID fid = env->GetFieldID(c, "isBoolean", "Z"); - env->SetBooleanField(optval, fid, true); - fid = env->GetFieldID(c, "booleanValue", "Z"); - env->SetBooleanField(optval, fid, (bool)optval_int); - } - if (optname == IP_TTL - || optname == SO_RCVTIMEO - || optname == IP_TOS - || optname == SO_LINGER - || optname == SO_RCVBUF - || optname == SO_SNDBUF) - { - jfieldID fid = env->GetFieldID(c, "isInteger", "Z"); - env->SetBooleanField(optval, fid, true); - fid = env->GetFieldID(c, "integerValue", "I"); - env->SetIntField(optval, fid, optval_int); - } - return retval > -1 ? retval : -(zts_errno); -} - -JNIEXPORT jboolean JNICALL Java_com_zerotier_libzt_ZeroTier_getsockname(JNIEnv *env, jobject thisObj, - jint fd, jobject addr) -{ - 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); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_getpeername(JNIEnv *env, jobject thisObj, - jint fd, jobject addr) -{ - 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); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_close( - JNIEnv *env, jobject thisObj, jint fd) -{ - return zts_close(fd); -} - -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 zts_timeval _timeout; - _timeout.tv_sec = timeout_sec; - _timeout.tv_usec = timeout_usec; - 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); - } - if (writefds) { - w = &_writefds; - ztfdset2fdset(env, nfds, writefds, &_writefds); - } - if (exceptfds) { - e = &_exceptfds; - ztfdset2fdset(env, nfds, exceptfds, &_exceptfds); - } - int retval = zts_select(nfds, r, w, e, &_timeout); - if (readfds) { - fdset2ztfdset(env, nfds, &_readfds, readfds); - } - if (writefds) { - fdset2ztfdset(env, nfds, &_writefds, writefds); - } - if (exceptfds) { - fdset2ztfdset(env, nfds, &_exceptfds, exceptfds); - } - return retval > -1 ? retval : -(zts_errno); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_fcntl( - JNIEnv *env, jobject thisObj, jint fd, jint cmd, jint flags) -{ - int retval = zts_fcntl(fd, cmd, flags); - return retval > -1 ? retval : -(zts_errno); -} - -JNIEXPORT int JNICALL Java_com_zerotier_libzt_ZeroTier_ioctl( - JNIEnv *env, jobject thisObj, jint fd, jlong request, jobject argp) -{ - int retval = ZTS_ERR_OK; - if (request == 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_ARG; - } - jfieldID fid = env->GetFieldID(c, "integer", "I"); - env->SetIntField(argp, fid, bytesRemaining); - } - if (request == FIONBIO) { - // TODO: double check - int meaninglessVariable = 0; - retval = zts_ioctl(fd, request, &meaninglessVariable); - } - return retval > -1 ? retval : -(zts_errno); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_send( - JNIEnv *env, jobject thisObj, jint fd, jbyteArray buf, int flags) -{ - void *data = env->GetPrimitiveArrayCritical(buf, NULL); - int retval = zts_send(fd, data, env->GetArrayLength(buf), flags); - env->ReleasePrimitiveArrayCritical(buf, data, 0); - return retval > -1 ? retval : -(zts_errno); -} - -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 zts_sockaddr_storage ss; - zta2ss(env, &ss, addr); - 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); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_recv(JNIEnv *env, jobject thisObj, - jint fd, jbyteArray buf, jint flags) -{ - void *data = env->GetPrimitiveArrayCritical(buf, NULL); - int retval = zts_recv(fd, data, env->GetArrayLength(buf), flags); - env->ReleasePrimitiveArrayCritical(buf, data, 0); - return retval > -1 ? retval : -(zts_errno); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_recvfrom( - JNIEnv *env, jobject thisObj, jint fd, jbyteArray buf, jint flags, jobject addr) -{ - 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 zts_sockaddr *)&ss, &addrlen); - env->ReleasePrimitiveArrayCritical(buf, data, 0); - ss2zta(env, &ss, addr); - return retval > -1 ? retval : -(zts_errno); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_read(JNIEnv *env, jobject thisObj, - jint fd, jbyteArray buf) -{ - void *data = env->GetPrimitiveArrayCritical(buf, NULL); - int retval = zts_read(fd, data, env->GetArrayLength(buf)); - env->ReleasePrimitiveArrayCritical(buf, data, 0); - return retval > -1 ? retval : -(zts_errno); -} - -ssize_t zts_read_offset(int fd, void *buf, size_t offset, size_t len) -{ - if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { - return ZTS_ERR_SERVICE; - } - if (!buf) { - return ZTS_ERR_ARG; - } - char *cbuf = (char*)buf; - return lwip_read(fd, &(cbuf[offset]), len); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_read_1offset(JNIEnv *env, jobject thisObj, - jint fd, jbyteArray buf, jint offset, jint len) -{ - void *data = env->GetPrimitiveArrayCritical(buf, NULL); - int retval = zts_read_offset(fd, data, offset, len); - env->ReleasePrimitiveArrayCritical(buf, data, 0); - return retval > -1 ? retval : -(zts_errno); -} -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_read_1length(JNIEnv *env, jobject thisObj, - jint fd, jbyteArray buf, jint len) -{ - void *data = env->GetPrimitiveArrayCritical(buf, NULL); - int retval = zts_read(fd, data, len); - env->ReleasePrimitiveArrayCritical(buf, data, 0); - return retval > -1 ? retval : -(zts_errno); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_write__IB(JNIEnv *env, jobject thisObj, - jint fd, jbyteArray buf) -{ - void *data = env->GetPrimitiveArrayCritical(buf, NULL); - int retval = zts_write(fd, data, env->GetArrayLength(buf)); - env->ReleasePrimitiveArrayCritical(buf, data, 0); - return retval > -1 ? retval : -(zts_errno); -} -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_write_1offset(JNIEnv *env, jobject thisObj, - jint fd, jbyteArray buf, jint offset, jint len) -{ - void *data = env->GetPrimitiveArrayCritical(&(buf[offset]), NULL); // PENDING: check? - int retval = zts_write(fd, data, len); - env->ReleasePrimitiveArrayCritical(buf, data, 0); - return retval > -1 ? retval : -(zts_errno); -} -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_write_1byte(JNIEnv *env, jobject thisObj, - jint fd, jbyte buf) -{ - int retval = zts_write(fd, &buf, 1); - return retval > -1 ? retval : -(zts_errno); -} - -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_shutdown( - JNIEnv *env, jobject thisObj, int fd, int how) -{ - return zts_shutdown(fd, how); -} - -#ifdef ZTS_ENABLE_STATS - -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 // ZTS_ENABLE_STATS - -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; - } - 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, zts_fd_set *src_fd_set, jobject dest_ztfd_set) -{ - jclass c = env->GetObjectClass(dest_ztfd_set); - if (!c) { - return; - } - jfieldID fid = env->GetFieldID(c, "fds_bits", "[B"); - jobject fdData = env->GetObjectField (dest_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; -} - -////////////////////////////////////////////////////////////////////////////// -// Helpers (for moving data across the JNI barrier) // -////////////////////////////////////////////////////////////////////////////// - -void ss2zta(JNIEnv *env, struct zts_sockaddr_storage *ss, jobject addr) -{ - jclass c = env->GetObjectClass(addr); - if (!c) { - return; - } - if(ss->ss_family == ZTS_AF_INET) - { - 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"); - env->SetIntField(addr, fid, (in4->sin_family)); - fid = env->GetFieldID(c, "_ip4", "[B"); - jobject ipData = env->GetObjectField (addr, fid); - jbyteArray * arr = reinterpret_cast(&ipData); - char *data = (char*)env->GetByteArrayElements(*arr, NULL); - memcpy(data, &(in4->sin_addr.s_addr), 4); - env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); - - return; - } - if(ss->ss_family == ZTS_AF_INET6) - { - 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"); - env->SetIntField(addr, fid, (in6->sin6_family)); - fid = env->GetFieldID(c, "_ip6", "[B"); - jobject ipData = env->GetObjectField (addr, fid); - jbyteArray * arr = reinterpret_cast(&ipData); - char *data = (char*)env->GetByteArrayElements(*arr, NULL); - memcpy(data, &(in6->sin6_addr.s6_addr), 16); - env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); - return; - } -} - -void zta2ss(JNIEnv *env, struct zts_sockaddr_storage *ss, jobject addr) -{ - jclass c = env->GetObjectClass(addr); - if (!c) { - return; - } - jfieldID fid = env->GetFieldID(c, "_family", "I"); - int family = env->GetIntField(addr, fid); - if (family == ZTS_AF_INET) - { - 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 = ZTS_AF_INET; - fid = env->GetFieldID(c, "_ip4", "[B"); - jobject ipData = env->GetObjectField (addr, fid); - jbyteArray * arr = reinterpret_cast(&ipData); - char *data = (char*)env->GetByteArrayElements(*arr, NULL); - memcpy(&(in4->sin_addr.s_addr), data, 4); - env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); - return; - } - if (family == ZTS_AF_INET6) - { - 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 = ZTS_AF_INET6; - fid = env->GetFieldID(c, "_ip6", "[B"); - jobject ipData = env->GetObjectField (addr, fid); - jbyteArray * arr = reinterpret_cast(&ipData); - char *data = (char*)env->GetByteArrayElements(*arr, NULL); - memcpy(&(in6->sin6_addr.s6_addr), data, 16); - env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); - return; - } -} - -#ifdef __cplusplus -} -#endif - -} // namespace ZeroTier - -#endif // ZTS_ENABLE_JAVA - diff --git a/src/bindings/java/JavaSockets.cxx b/src/bindings/java/JavaSockets.cxx new file mode 100644 index 0000000..899dfe3 --- /dev/null +++ b/src/bindings/java/JavaSockets.cxx @@ -0,0 +1,1192 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +/** + * @file + * + * ZeroTier C Java Socket API + */ + +#ifdef ZTS_ENABLE_JAVA + +#include "Events.hpp" +#include "ZeroTierSockets.h" +#include "lwip/def.h" +#include "lwip/inet.h" +#include "lwip/sockets.h" +#include "lwip/stats.h" + +#include + +extern int zts_errno; + +namespace ZeroTier { + +extern uint8_t _state; +extern JavaVM* jvm; + +void java_detach_from_thread() +{ + jint rs = jvm->DetachCurrentThread(); +} + +#ifdef __cplusplus +extern "C" { +#endif + +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); + +/* + * 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_sdk_ZeroTierNative_zts_1init(JNIEnv* env, jobject thisObj) +{ + jint rs = env->GetJavaVM(&jvm); + return rs != JNI_OK ? ZTS_ERR_GENERAL : ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1socket(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 into return value for JNI functions only +} + +/* +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1connect(JNIEnv* env, jobject thisObj, jint fd, jobject addr) +{ + struct zts_sockaddr_storage ss; + zta2ss(env, &ss, addr); + 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); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1bind(JNIEnv* env, jobject thisObj, jint fd, jobject addr) +{ + struct zts_sockaddr_storage ss; + zta2ss(env, &ss, addr); + 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); +} +*/ + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1listen(JNIEnv* env, jobject thisObj, jint fd, int backlog) +{ + int retval = zts_listen(fd, backlog); + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1accept(JNIEnv* env, jobject thisObj, jint fd, jobject addr, jint port) +{ + 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); +} +/* +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1setsockopt( + JNIEnv* env, + jobject thisObj, + jint fd, + jint level, + jint optname, + jobject optval) +{ + jclass c = env->GetObjectClass(optval); + if (! c) { + return ZTS_ERR_SERVICE; + } + int optval_int = -1; + + if (optname == SO_BROADCAST || optname == SO_KEEPALIVE || optname == SO_REUSEADDR || optname == SO_REUSEPORT + || optname == TCP_NODELAY) { + jfieldID fid = env->GetFieldID(c, "booleanValue", "Z"); + optval_int = (int)(env->GetBooleanField(optval, fid)); + } + if (optname == IP_TTL || optname == SO_RCVTIMEO || optname == IP_TOS || optname == SO_LINGER || optname == SO_RCVBUF + || optname == SO_SNDBUF) { + jfieldID fid = env->GetFieldID(c, "integerValue", "I"); + optval_int = env->GetIntField(optval, fid); + } + + int retval = ZTS_ERR_OK; + + if (optname == SO_RCVTIMEO) { + struct timeval tv; + // Convert milliseconds from setSoTimeout() call to seconds and microseconds + tv.tv_usec = optval_int * 1000; + tv.tv_sec = optval_int / 1000000; + retval = zts_setsockopt(fd, level, optname, &tv, sizeof(tv)); + } + else { + retval = zts_setsockopt(fd, level, optname, &optval_int, sizeof(optval_int)); + } + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1getsockopt( + JNIEnv* env, + jobject thisObj, + jint fd, + jint level, + jint optname, + jobject optval) +{ + jclass c = env->GetObjectClass(optval); + if (! c) { + return ZTS_ERR_SERVICE; + } + int optval_int = 0; + zts_socklen_t optlen; // Intentionally not used + + int retval; + + if (optname == SO_RCVTIMEO) { + struct zts_timeval tv; + optlen = sizeof(tv); + retval = zts_getsockopt(fd, level, optname, &tv, &optlen); + // Convert seconds and microseconds back to milliseconds + optval_int = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + } + else { + retval = zts_getsockopt(fd, level, optname, &optval_int, &optlen); + } + + if (optname == SO_BROADCAST || optname == SO_KEEPALIVE || optname == SO_REUSEADDR || optname == SO_REUSEPORT + || optname == TCP_NODELAY) { + jfieldID fid = env->GetFieldID(c, "isBoolean", "Z"); + env->SetBooleanField(optval, fid, true); + fid = env->GetFieldID(c, "booleanValue", "Z"); + env->SetBooleanField(optval, fid, (bool)optval_int); + } + if (optname == IP_TTL || optname == SO_RCVTIMEO || optname == IP_TOS || optname == SO_LINGER || optname == SO_RCVBUF + || optname == SO_SNDBUF) { + jfieldID fid = env->GetFieldID(c, "isInteger", "Z"); + env->SetBooleanField(optval, fid, true); + fid = env->GetFieldID(c, "integerValue", "I"); + env->SetIntField(optval, fid, optval_int); + } + return retval > -1 ? retval : -(zts_errno); +} +*/ + +JNIEXPORT jboolean JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1getsockname(JNIEnv* env, jobject thisObj, jint fd, jobject addr) +{ + 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); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1getpeername(JNIEnv* env, jobject thisObj, jint fd, jobject addr) +{ + 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); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1close(JNIEnv* env, jobject thisObj, jint fd) +{ + return zts_close(fd); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1select( + JNIEnv* env, + jobject thisObj, + jint nfds, + jobject readfds, + jobject writefds, + jobject exceptfds, + jint timeout_sec, + jint timeout_usec) +{ + struct zts_timeval _timeout; + _timeout.tv_sec = timeout_sec; + _timeout.tv_usec = timeout_usec; + 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); + } + if (writefds) { + w = &_writefds; + ztfdset2fdset(env, nfds, writefds, &_writefds); + } + if (exceptfds) { + e = &_exceptfds; + ztfdset2fdset(env, nfds, exceptfds, &_exceptfds); + } + int retval = zts_select(nfds, r, w, e, &_timeout); + if (readfds) { + fdset2ztfdset(env, nfds, &_readfds, readfds); + } + if (writefds) { + fdset2ztfdset(env, nfds, &_writefds, writefds); + } + if (exceptfds) { + fdset2ztfdset(env, nfds, &_exceptfds, exceptfds); + } + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1fcntl(JNIEnv* env, jobject thisObj, jint fd, jint cmd, jint flags) +{ + int retval = zts_fcntl(fd, cmd, flags); + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT int JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1ioctl(JNIEnv* env, jobject thisObj, jint fd, jlong request, jobject argp) +{ + int retval = ZTS_ERR_OK; + if (request == 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_ARG; + } + jfieldID fid = env->GetFieldID(c, "integer", "I"); + env->SetIntField(argp, fid, bytesRemaining); + } + if (request == FIONBIO) { + // TODO: double check + int meaninglessVariable = 0; + retval = zts_ioctl(fd, request, &meaninglessVariable); + } + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1send(JNIEnv* env, jobject thisObj, jint fd, jbyteArray buf, int flags) +{ + void* data = env->GetPrimitiveArrayCritical(buf, NULL); + int retval = zts_send(fd, data, env->GetArrayLength(buf), flags); + env->ReleasePrimitiveArrayCritical(buf, data, 0); + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1sendto( + JNIEnv* env, + jobject thisObj, + jint fd, + jbyteArray buf, + jint flags, + jobject addr) +{ + void* data = env->GetPrimitiveArrayCritical(buf, NULL); + struct zts_sockaddr_storage ss; + zta2ss(env, &ss, addr); + 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); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1recv(JNIEnv* env, jobject thisObj, jint fd, jbyteArray buf, jint flags) +{ + void* data = env->GetPrimitiveArrayCritical(buf, NULL); + int retval = zts_recv(fd, data, env->GetArrayLength(buf), flags); + env->ReleasePrimitiveArrayCritical(buf, data, 0); + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1recvfrom( + JNIEnv* env, + jobject thisObj, + jint fd, + jbyteArray buf, + jint flags, + jobject addr) +{ + 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 zts_sockaddr*)&ss, &addrlen); + env->ReleasePrimitiveArrayCritical(buf, data, 0); + ss2zta(env, &ss, addr); + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1read(JNIEnv* env, jobject thisObj, jint fd, jbyteArray buf) +{ + void* data = env->GetPrimitiveArrayCritical(buf, NULL); + int retval = zts_read(fd, data, env->GetArrayLength(buf)); + env->ReleasePrimitiveArrayCritical(buf, data, 0); + return retval > -1 ? retval : -(zts_errno); +} + +ssize_t zts_read_offset(int fd, void* buf, size_t offset, size_t len) +{ + char* cbuf = (char*)buf; + return zts_read(fd, &(cbuf[offset]), len); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1read_1offset( + JNIEnv* env, + jobject thisObj, + jint fd, + jbyteArray buf, + jint offset, + jint len) +{ + void* data = env->GetPrimitiveArrayCritical(buf, NULL); + int retval = zts_read_offset(fd, data, offset, len); + env->ReleasePrimitiveArrayCritical(buf, data, 0); + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1read_1length(JNIEnv* env, jobject thisObj, jint fd, jbyteArray buf, jint len) +{ + void* data = env->GetPrimitiveArrayCritical(buf, NULL); + int retval = zts_read(fd, data, len); + env->ReleasePrimitiveArrayCritical(buf, data, 0); + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1write__IB(JNIEnv* env, jobject thisObj, jint fd, jbyteArray buf) +{ + void* data = env->GetPrimitiveArrayCritical(buf, NULL); + int retval = zts_write(fd, data, env->GetArrayLength(buf)); + env->ReleasePrimitiveArrayCritical(buf, data, 0); + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1write_1offset( + JNIEnv* env, + jobject thisObj, + jint fd, + jbyteArray buf, + jint offset, + jint len) +{ + void* data = env->GetPrimitiveArrayCritical(&(buf[offset]), NULL); // PENDING: check? + int retval = zts_write(fd, data, len); + env->ReleasePrimitiveArrayCritical(buf, data, 0); + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1write_1byte(JNIEnv* env, jobject thisObj, jint fd, jbyte buf) +{ + int retval = zts_write(fd, &buf, 1); + return retval > -1 ? retval : -(zts_errno); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1shutdown(JNIEnv* env, jobject thisObj, int fd, int how) +{ + return zts_shutdown(fd, how); +} + +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; + } + 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; i < nfds; i++) { + if (data[i] == 0x01) { + ZTS_FD_SET(i, dest_fd_set); + } + } + env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); + return; +} + +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) { + return; + } + jfieldID fid = env->GetFieldID(c, "fds_bits", "[B"); + jobject fdData = env->GetObjectField(dest_ztfd_set, fid); + jbyteArray* arr = reinterpret_cast(&fdData); + char* data = (char*)env->GetByteArrayElements(*arr, NULL); + for (int i = 0; i < nfds; i++) { + if (ZTS_FD_ISSET(i, src_fd_set)) { + data[i] = 0x01; + } + } + env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); + return; +} + +//----------------------------------------------------------------------------// +// Helpers (for moving data across the JNI barrier) // +//----------------------------------------------------------------------------// + +void ss2zta(JNIEnv* env, struct zts_sockaddr_storage* ss, jobject addr) +{ + jclass c = env->GetObjectClass(addr); + if (! c) { + return; + } + if (ss->ss_family == ZTS_AF_INET) { + 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"); + env->SetIntField(addr, fid, (in4->sin_family)); + fid = env->GetFieldID(c, "_ip4", "[B"); + jobject ipData = env->GetObjectField(addr, fid); + jbyteArray* arr = reinterpret_cast(&ipData); + char* data = (char*)env->GetByteArrayElements(*arr, NULL); + memcpy(data, &(in4->sin_addr.s_addr), 4); + env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); + + return; + } + if (ss->ss_family == ZTS_AF_INET6) { + 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"); + env->SetIntField(addr, fid, (in6->sin6_family)); + fid = env->GetFieldID(c, "_ip6", "[B"); + jobject ipData = env->GetObjectField(addr, fid); + jbyteArray* arr = reinterpret_cast(&ipData); + char* data = (char*)env->GetByteArrayElements(*arr, NULL); + memcpy(data, &(in6->sin6_addr.s6_addr), 16); + env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); + return; + } +} + +void zta2ss(JNIEnv* env, struct zts_sockaddr_storage* ss, jobject addr) +{ + jclass c = env->GetObjectClass(addr); + if (! c) { + return; + } + jfieldID fid = env->GetFieldID(c, "_family", "I"); + int family = env->GetIntField(addr, fid); + if (family == ZTS_AF_INET) { + 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 = ZTS_AF_INET; + fid = env->GetFieldID(c, "_ip4", "[B"); + jobject ipData = env->GetObjectField(addr, fid); + jbyteArray* arr = reinterpret_cast(&ipData); + char* data = (char*)env->GetByteArrayElements(*arr, NULL); + memcpy(&(in4->sin_addr.s_addr), data, 4); + env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); + return; + } + if (family == ZTS_AF_INET6) { + 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 = ZTS_AF_INET6; + fid = env->GetFieldID(c, "_ip6", "[B"); + jobject ipData = env->GetObjectField(addr, fid); + jbyteArray* arr = reinterpret_cast(&ipData); + char* data = (char*)env->GetByteArrayElements(*arr, NULL); + memcpy(&(in6->sin6_addr.s6_addr), data, 16); + env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); + return; + } +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1node_1get_1port(JNIEnv* jenv, jobject thisObj) +{ + return zts_node_get_port(); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1node_1stop(JNIEnv* jenv, jobject thisObj) +{ + int res = zts_node_stop(); + java_detach_from_thread(); + return res; +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1node_1free(JNIEnv* jenv, jobject thisObj) +{ + int res = zts_node_free(); + java_detach_from_thread(); + return res; +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1net_1join(JNIEnv* env, jobject thisObj, jlong net_id) +{ + return zts_net_join((uint64_t)net_id); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1net_1leave(JNIEnv* env, jobject thisObj, jlong net_id) +{ + return zts_net_leave((uint64_t)net_id); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1id_1new(JNIEnv* jenv, jobject thisObj, char* key, int* key_buf_len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1id_1pair_1is_1valid(JNIEnv* jenv, jobject thisObj, char* key, int len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1init_1from_1storage(JNIEnv* jenv, jobject thisObj, jstring path) +{ + if (! path) { + return ZTS_ERR_ARG; + } + const char* utf_string = jenv->GetStringUTFChars(path, NULL); + if (! utf_string) { + return ZTS_ERR_GENERAL; + } + int retval = zts_init_from_storage(utf_string); + jenv->ReleaseStringUTFChars(path, utf_string); + return retval; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1init_1set_1event_1handler(JNIEnv* env, jobject thisObj, jobject callback) +{ + jclass eventListenerClass = env->GetObjectClass(callback); + if (eventListenerClass == NULL) { + fprintf(stderr, "Couldn't find class for ZeroTierEventListener instance"); + return ZTS_ERR_ARG; + } + jmethodID eventListenerCallbackMethod = env->GetMethodID(eventListenerClass, "onZeroTierEvent", "(JI)V"); + if (eventListenerCallbackMethod == NULL) { + fprintf(stderr, "Couldn't find onZeroTierEvent method"); + return ZTS_ERR_ARG; + } + return zts_init_set_event_handler(env->NewGlobalRef(callback), eventListenerCallbackMethod); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1init_1set_1port(JNIEnv* jenv, jobject thisObj, short port) +{ + return zts_init_set_port(port); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1init_1from_1memory(JNIEnv* jenv, jobject thisObj, char* key, int len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1init_1blacklist_1if(JNIEnv* jenv, jobject thisObj, jstring prefix, jint len) +{ + if (! prefix) { + return ZTS_ERR_ARG; + } + const char* utf_string = jenv->GetStringUTFChars(prefix, NULL); + if (! utf_string) { + return ZTS_ERR_GENERAL; + } + int retval = zts_init_blacklist_if(utf_string, len); + jenv->ReleaseStringUTFChars(prefix, utf_string); + return retval; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1init_1set_roots(JNIEnv* jenv, jobject thisObj, void* roots_data, jint len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1init_1allow_1net_1cache(JNIEnv* jenv, jobject thisObj, jint allowed) +{ + return zts_init_allow_net_cache(allowed); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1init_1allow_1peer_1cache(JNIEnv* jenv, jobject thisObj, jint allowed) +{ + return zts_init_allow_peer_cache(allowed); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1init_1allow_1roots_1cache(JNIEnv* jenv, jobject thisObj, jint allowed) +{ + return zts_init_allow_roots_cache(allowed); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1init_1allow_1id_1cache(JNIEnv* jenv, jobject thisObj, jint allowed) +{ + return zts_init_allow_id_cache(allowed); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1addr_1is_1assigned(JNIEnv* jenv, jobject thisObj, jlong net_id, jint family) +{ + return zts_addr_is_assigned(net_id, family); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1addr_1get( + JNIEnv* jenv, + jobject thisObj, + long net_id, + jint family, + struct sockaddr_storage* addr) +{ +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1addr_1get_1str( + JNIEnv* jenv, + jobject thisObj, + long net_id, + jint family, + char* dst, + jint len) +{ +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1addr_1get_1all( + JNIEnv* jenv, + jobject thisObj, + long net_id, + struct sockaddr_storage* addr, + jint* count) +{ +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1addr_1compute_16plane( + JNIEnv* jenv, + jobject thisObj, + jlong net_id, + jlong node_id, + struct sockaddr_storage* addr) +{ +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1addr_1compute_1rfc4193( + JNIEnv* jenv, + jobject thisObj, + jlong net_id, + jlong node_id, + struct sockaddr_storage* addr) +{ +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1addr_1compute_1rfc4193_1str( + JNIEnv* jenv, + jobject thisObj, + jlong net_id, + jlong node_id, + jstring dst, + jint len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1addr_1compute_16plane_1str( + JNIEnv* jenv, + jobject thisObj, + jlong net_id, + jlong node_id, + jstring dst, + jint len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT uint64_t JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1net_1compute_1adhoc_1id( + JNIEnv* jenv, + jobject thisObj, + short start_port, + short end_port) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1net_1transport_1is_1ready(JNIEnv* jenv, jobject thisObj, jlong net_id) +{ + return zts_net_transport_is_ready(net_id); +} + +JNIEXPORT uint64_t JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1net_get_mac(JNIEnv* jenv, jobject thisObj, jlong net_id) +{ + return zts_net_get_mac(net_id); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1net_1get_1mac_1str( + JNIEnv* jenv, + jobject thisObj, + jlong net_id, + jstring dst, + jint len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1net_1get_1broadcast(JNIEnv* jenv, jobject thisObj, jlong net_id) +{ + return zts_net_get_broadcast(net_id); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1net_1get_1mtu(JNIEnv* jenv, jobject thisObj, jlong net_id) +{ + return zts_net_get_mtu(net_id); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1net_1get_1name( + JNIEnv* jenv, + jobject thisObj, + jlong net_id, + jstring dst, + jint len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1net_1get_1status(JNIEnv* jenv, jobject thisObj, jlong net_id) +{ + return zts_net_get_status(net_id); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1net_1get_1type(JNIEnv* jenv, jobject thisObj, jlong net_id) +{ + return zts_net_get_type(net_id); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1route_1is_1assigned(JNIEnv* jenv, jobject thisObj, jlong net_id, jint family) +{ + return zts_route_is_assigned(net_id, family); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1node_1start(JNIEnv* jenv, jobject thisObj) +{ + return zts_node_start(); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1node_1is_1online(JNIEnv* jenv, jobject thisObj) +{ + return zts_node_is_online(); +} + +JNIEXPORT uint64_t JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1node_1get_1id(JNIEnv* jenv, jobject thisObj) +{ + return zts_node_get_id(); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1node_1get_1id_1pair( + JNIEnv* jenv, + jobject thisObj, + char* key, + jint* key_buf_len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1moon_1orbit( + JNIEnv* jenv, + jobject thisObj, + jlong moon_roots_id, + jlong moon_seed) +{ + return zts_moon_orbit(moon_roots_id, moon_seed); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1moon_1deorbit(JNIEnv* jenv, jobject thisObj, jlong moon_roots_id) +{ + return zts_moon_deorbit(moon_roots_id); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1connect( + JNIEnv* jenv, + jobject thisObj, + jint fd, + jstring ipstr, + jint port, + jint timeout_ms) +{ + if (! ipstr) { + return ZTS_ERR_ARG; + } + const char* utf_string = jenv->GetStringUTFChars(ipstr, NULL); + if (! utf_string) { + return ZTS_ERR_GENERAL; + } + int retval = zts_simple_connect(fd, utf_string, port, timeout_ms); + jenv->ReleaseStringUTFChars(ipstr, utf_string); + return retval; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1bind(JNIEnv* jenv, jobject thisObj, jint fd, jstring ipstr, jint port) +{ + if (! ipstr) { + return ZTS_ERR_ARG; + } + const char* utf_string = jenv->GetStringUTFChars(ipstr, NULL); + if (! utf_string) { + return ZTS_ERR_GENERAL; + } + int retval = zts_simple_bind(fd, utf_string, port); + jenv->ReleaseStringUTFChars(ipstr, utf_string); + return retval; +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1accept( + JNIEnv* jenv, + jobject thisObj, + int fd, + jstring remote_addr, + jint len, + jint* port) +{ + /* + if (! remote_addr) { + return ZTS_ERR_ARG; + } + const char* utf_string = jenv->GetStringUTFChars(remote_addr, NULL); + if (! utf_string) { + return ZTS_ERR_GENERAL; + } + int retval = zts_simple_bind(fd, utf_string, port); + jenv->ReleaseStringUTFChars(ipstr, utf_string); + return retval; + + + jstr = (*env)->NewStringUTF(env, greeting); + + return jstr; + + */ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1udp_1client(JNIEnv* jenv, jobject thisObj, jstring remote_ipstr) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1set_1no_1delay(JNIEnv* jenv, jobject thisObj, jint fd, jint enabled) +{ + return zts_simple_set_no_delay(fd, enabled); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1no_1delay(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_no_delay(fd); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1set_1linger( + JNIEnv* jenv, + jobject thisObj, + jint fd, + jint enabled, + jint value) +{ + return zts_simple_set_linger(fd, enabled, value); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1linger_1enabled(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_linger_enabled(fd); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1linger_1value(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_linger_value(fd); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1pending_1data_1size(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_pending_data_size(fd); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1set_1reuse_1addr(JNIEnv* jenv, jobject thisObj, jint fd, jint enabled) +{ + return zts_simple_set_reuse_addr(fd, enabled); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1reuse_1addr(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_reuse_addr(fd); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1set_1recv_1timeout( + JNIEnv* jenv, + jobject thisObj, + jint fd, + jint seconds, + jint microseconds) +{ + return zts_simple_set_recv_timeout(fd, seconds, microseconds); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1recv_1timeout(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_recv_timeout(fd); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1set_1send_1timeout( + JNIEnv* jenv, + jobject thisObj, + jint fd, + jint seconds, + jint microseconds) +{ + return zts_simple_set_send_timeout(fd, seconds, microseconds); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1send_1timeout(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_send_timeout(fd); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1set_1send_1buf_1size( + JNIEnv* jenv, + jobject thisObj, + jint fd, + jint size) +{ + return zts_simple_set_send_buf_size(fd, size); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1send_1buf_1size(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_send_buf_size(fd); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1set_1recv_1buf_1size( + JNIEnv* jenv, + jobject thisObj, + jint fd, + jint size) +{ + return zts_simple_set_recv_buf_size(fd, size); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1recv_1buf_1size(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_recv_buf_size(fd); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1set_1ttl(JNIEnv* jenv, jobject thisObj, jint fd, jint ttl) +{ + return zts_simple_set_ttl(fd, ttl); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1ttl(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_ttl(fd); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1set_1blocking(JNIEnv* jenv, jobject thisObj, jint fd, jint enabled) +{ + return zts_simple_set_blocking(fd, enabled); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1blocking(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_blocking(fd); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1set_1keepalive(JNIEnv* jenv, jobject thisObj, jint fd, jint enabled) +{ + return zts_simple_set_keepalive(fd, enabled); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1simple_1get_1keepalive(JNIEnv* jenv, jobject thisObj, jint fd) +{ + return zts_simple_get_keepalive(fd); +} + +struct hostent* Java_com_zerotier_sdk_ZeroTierNative_zts_1gethostbyname(JNIEnv* jenv, jobject thisObj, jstring name) +{ + return NULL; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1dns_1set_1server(JNIEnv* jenv, jobject thisObj, uint8_t index, ip_addr* addr) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT ip_addr* JNICALL dns_1get_1server(JNIEnv* jenv, jobject thisObj, uint8_t index) +{ + return NULL; +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1core_1lock_1obtain(JNIEnv* jenv, jobject thisObj) +{ + return zts_core_lock_obtain(); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1core_1lock_1release(JNIEnv* jenv, jobject thisObj) +{ + return zts_core_lock_release(); +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1core_1query_1addr_1count(JNIEnv* jenv, jobject thisObj, jlong net_id) +{ + return zts_core_query_addr_count(net_id); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1core_1query_1addr( + JNIEnv* jenv, + jobject thisObj, + jlong net_id, + jint idx, + jstring addr, + jint len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1core_1query_1route_1count(JNIEnv* jenv, jobject thisObj, jlong net_id) +{ + return zts_core_query_route_count(net_id); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1core_1query_1route( + JNIEnv* jenv, + jobject thisObj, + jlong net_id, + jint idx, + jstring target, + jstring via, + jint len, + short* flags, + short* metric) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1core_1query_1path_1count(JNIEnv* jenv, jobject thisObj, jlong peer_id) +{ + return zts_core_query_path_count(peer_id); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1core_1query_1path( + JNIEnv* jenv, + jobject thisObj, + jlong peer_id, + jint idx, + jstring dst, + jint len) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1core_1query_1mc_1count(JNIEnv* jenv, jobject thisObj, jlong net_id) +{ + return zts_core_query_mc_count(net_id); +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1core_1query_1mc( + JNIEnv* jenv, + jobject thisObj, + long net_id, + jint idx, + jlong* mac, + uint32_t* adi) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT jint JNICALL Java_com_zerotier_sdk_ZeroTierNative_zts_1util_1roots_1new( + JNIEnv* jenv, + jobject thisObj, + char* roots_out, + jint* roots_len, + char* prev_key, + jint* prev_key_len, + char* curr_key, + jint* curr_key_len, + jlong id, + jlong ts, + zts_root_set_t* roots_spec) +{ + return ZTS_ERR_OK; +} + +JNIEXPORT void JNICALL +Java_com_zerotier_sdk_ZeroTierNative_zts_1util_1delay(JNIEnv* jenv, jobject thisObj, jlong milliseconds) +{ + zts_util_delay(milliseconds); +} + +#ifdef __cplusplus +} +#endif + +} // namespace ZeroTier + +#endif // ZTS_ENABLE_JAVA diff --git a/src/bindings/java/README.md b/src/bindings/java/README.md new file mode 100644 index 0000000..d71d0b7 --- /dev/null +++ b/src/bindings/java/README.md @@ -0,0 +1,4 @@ +Java Language Bindings +===== + +See [examples/java](./../../../examples/java/) for usage. \ No newline at end of file diff --git a/src/bindings/java/ZeroTierEventListener.java b/src/bindings/java/ZeroTierEventListener.java new file mode 100644 index 0000000..6e8ef10 --- /dev/null +++ b/src/bindings/java/ZeroTierEventListener.java @@ -0,0 +1,24 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package com.zerotier.sdk; + +/** + * Class that must be implemented to receive ZeroTier event notifications. + */ +interface ZeroTierEventListener { + /* + * Called when an even occurs within ZeroTier + */ + public void onZeroTierEvent(long nwid, int event_code); +} diff --git a/src/bindings/java/ZeroTierFileDescriptorSet.java b/src/bindings/java/ZeroTierFileDescriptorSet.java new file mode 100644 index 0000000..fa22cfd --- /dev/null +++ b/src/bindings/java/ZeroTierFileDescriptorSet.java @@ -0,0 +1,55 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package com.zerotier.sdk; + +/** + * This class provides an fdset-like behavior to ZeroTierSocket + */ +class ZeroTierFileDescriptorSet { + byte[] fds_bits = new byte[1024]; + + /** + * Clear bit + */ + public void CLR(int fd) + { + fds_bits[fd] = 0x00; + } + + /** + * Check if bit is set + */ + public boolean ISSET(int fd) + { + return fds_bits[fd] == 0x01; + } + + /** + * Set bit + */ + public void SET(int fd) + { + fds_bits[fd] = 0x01; + } + + /** + * Zero-out all bits + */ + public void ZERO() + { + for (int i = 0; i < 1024; i++) { + fds_bits[i] = 0x00; + } + } +} diff --git a/src/bindings/java/ZeroTierInputStream.java b/src/bindings/java/ZeroTierInputStream.java new file mode 100644 index 0000000..e99f413 --- /dev/null +++ b/src/bindings/java/ZeroTierInputStream.java @@ -0,0 +1,209 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package com.zerotier.sdk; + +import com.zerotier.sdk.ZeroTierNative; +import java.io.*; +import java.util.Objects; + +/** + * Extends InputStream using ZeroTier as a transport + */ +public class ZeroTierInputStream extends InputStream { + /** + * File descriptor used by lower native layer + */ + public int zfd = -1; + + /** + * Close the ZeroTierInputStream + * @exception IOException when an I/O error occurs + */ + public void close​() throws IOException + { + /* Note: this operation currently only stops RX on a socket that is shared + between both I/OStreams. This means that closing this stream will only shutdown + that aspect of the socket but not actually close it and free resources. Resources + will be properly freed when the socket implementation's close() is called or if + both I/OStreams are closed separately */ + ZeroTierNative.zts_shutdown(zfd, ZeroTierNative.ZTS_SHUT_RD); + zfd = -1; + } + + /** + * Transfer bytes from this stream to another + * @param destStream Unused + * @return Number of bytes transferred + * @exception IOException when an I/O error occurs + */ + public long transferTo​(OutputStream destStream) throws IOException + { + Objects.requireNonNull(destStream, "destStream must not be null"); + int bytesTransferred = 0, bytesRead; + byte[] buf = new byte[8192]; + while (((bytesRead = ZeroTierNative.zts_read(zfd, buf)) >= 0)) { + destStream.write(buf, 0, bytesRead); + bytesTransferred += bytesRead; + } + return bytesTransferred; + } + + /** + * Read a single byte from the stream + * @return Single byte read + * @exception IOException when an I/O error occurs + */ + public int read​() throws IOException + { + byte[] buf = new byte[1]; + // Unlike a native read(), if nothing is read we should return -1 + int retval = ZeroTierNative.zts_read(zfd, buf); + if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) { + return -1; + } + if (retval < 0) { + throw new IOException("read​(), errno=" + retval); + } + return buf[0]; + } + + /** + * Read from stream into buffer + * @param destBuffer Destination buffer + * @return Number of bytes read + * @exception IOException when an I/O error occurs + */ + public int read​(byte[] destBuffer) throws IOException + { + Objects.requireNonNull(destBuffer, "input byte array must not be null"); + // Unlike a native read(), if nothing is read we should return -1 + int retval = ZeroTierNative.zts_read(zfd, destBuffer); + if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) { + return -1; + } + if (retval < 0) { + throw new IOException("read​(destBuffer), errno=" + retval); + } + return retval; + } + + /** + * Read from stream into buffer at offset + * @param destBuffer Destination buffer + * @param offset Where in the destination buffer bytes should be written + * @param numBytes Number of bytes to read + * @return Number of bytes read. + * @exception IOException when an I/O error occurs + */ + public int read​(byte[] destBuffer, int offset, int numBytes) throws IOException + { + Objects.requireNonNull(destBuffer, "input byte array must not be null"); + if (offset < 0) { + throw new IndexOutOfBoundsException("offset < 0"); + } + if (numBytes < 0) { + throw new IndexOutOfBoundsException("numBytes < 0"); + } + if (numBytes > (destBuffer.length - offset)) { + throw new IndexOutOfBoundsException("numBytes > (destBuffer.length - offset"); + } + if (numBytes == 0) { + return 0; + } + // Unlike a native read(), if nothing is read we should return -1 + int retval = ZeroTierNative.zts_read_offset(zfd, destBuffer, offset, numBytes); + if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) { + return -1; + } + if (retval < 0) { + throw new IOException("read​(destBuffer, offset, numBytes), errno=" + retval); + } + return retval; + } + + /** + * Read all available data from stream + * @return Array of bytes + * @exception IOException when an I/O error occurs + */ + public byte[] readAllBytes​() throws IOException + { + int pendingDataSize = ZeroTierNative.zts_simple_get_pending_data_size(zfd); + byte[] buf = new byte[pendingDataSize]; + int retval = ZeroTierNative.zts_read(zfd, buf); + if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) { + // No action needed + } + if (retval < 0) { + throw new IOException("readAllBytes​(), errno=" + retval); + } + return buf; + } + + /** + * Read a given number of bytes from the stream into a buffer + * @param destBuffer Destination buffer + * @param offset Where in the destination buffer bytes should be written + * @param numBytes Number of bytes to read + * @return Nothing. + * @exception IOException when an I/O error occurs + */ + public int readNBytes​(byte[] destBuffer, int offset, int numBytes) throws IOException + { + Objects.requireNonNull(destBuffer, "input byte array must not be null"); + if (offset < 0) { + throw new IndexOutOfBoundsException("offset < 0"); + } + if (numBytes < 0) { + throw new IndexOutOfBoundsException("numBytes < 0"); + } + if (numBytes > (destBuffer.length - offset)) { + throw new IndexOutOfBoundsException("numBytes > destBuffer.length - offset"); + } + if (numBytes == 0) { + return 0; + } + int retval = ZeroTierNative.zts_read_offset(zfd, destBuffer, offset, numBytes); + if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) { + // No action needed + } + if (retval < 0) { + throw new IOException("readNBytes​(destBuffer, offset, numBytes), errno=" + retval); + } + return retval; + } + + /** + * Skip a certain number of bytes + * @param numBytes Unused + * @return Nothing. + * @exception IOException when an I/O error occurs + */ + public long skip​(long numBytes) throws IOException + { + if (numBytes <= 0) { + return 0; + } + long bytesRemaining = numBytes, bytesRead; + int bufSize = (int)Math.min(2048, bytesRemaining); + byte[] buf = new byte[bufSize]; + while (bytesRemaining > 0) { + if ((bytesRead = ZeroTierNative.zts_read_length(zfd, buf, (int)Math.min(bufSize, bytesRemaining))) < 0) { + break; + } + bytesRemaining -= bytesRead; + } + return numBytes - bytesRemaining; + } +} diff --git a/src/bindings/java/ZeroTierNative.java b/src/bindings/java/ZeroTierNative.java new file mode 100644 index 0000000..1e1a5dc --- /dev/null +++ b/src/bindings/java/ZeroTierNative.java @@ -0,0 +1,603 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package com.zerotier.sdk; + +/** + * Class that exposes the low-level C socket interface provided by libzt. This + * can be used instead of the higher-level ZeroTierSocket API. + */ +public class ZeroTierNative { + // Only to be called by static initializer of this class + public static native int zts_init(); + + static + { + // Loads dynamic library at initialization time + System.loadLibrary("zt"); + if (zts_init() != ZeroTierNative.ZTS_ERR_OK) { + throw new ExceptionInInitializerError("JNI init() failed (see GetJavaVM())"); + } + } + + //----------------------------------------------------------------------------// + // Event codes // + //----------------------------------------------------------------------------// + + /** + * Node has been initialized + * + * This is the first event generated; and is always sent. It may occur + * before node's constructor returns. + * + */ + public static int ZTS_EVENT_NODE_UP = 200; + + /** + * Node is online -- at least one upstream node appears reachable + * + */ + public static int ZTS_EVENT_NODE_ONLINE = 201; + + /** + * Node is offline -- network does not seem to be reachable by any available + * strategy + * + */ + public static int ZTS_EVENT_NODE_OFFLINE = 202; + + /** + * Node is shutting down + * + * This is generated within Node's destructor when it is being shut down. + * It's done for convenience; since cleaning up other state in the event + * handler may appear more idiomatic. + * + */ + public static int ZTS_EVENT_NODE_DOWN = 203; + + /** + * A fatal error has occurred. One possible reason is: + * + * Your identity has collided with another node's ZeroTier address + * + * This happens if two different public keys both hash (via the algorithm + * in Identity::generate()) to the same 40-bit ZeroTier address. + * + * This is something you should "never" see; where "never" is defined as + * once per 2^39 new node initializations / identity creations. If you do + * see it; you're going to see it very soon after a node is first + * initialized. + * + * This is reported as an event rather than a return code since it's + * detected asynchronously via error messages from authoritative nodes. + * + * If this occurs; you must shut down and delete the node; delete the + * identity.secret record/file from the data store; and restart to generate + * a new identity. If you don't do this; you will not be able to communicate + * with other nodes. + * + * We'd automate this process; but we don't think silently deleting + * private keys or changing our address without telling the calling code + * is good form. It violates the principle of least surprise. + * + * You can technically get away with not handling this; but we recommend + * doing so in a mature reliable application. Besides; handling this + * condition is a good way to make sure it never arises. It's like how + * umbrellas prevent rain and smoke detectors prevent fires. They do; right? + * + * Meta-data: none + */ + public static int ZTS_EVENT_NODE_FATAL_ERROR = 204; + + /** Network ID does not correspond to a known network */ + public static int ZTS_EVENT_NETWORK_NOT_FOUND = 210; + /** The version of ZeroTier inside libzt is too old */ + public static int ZTS_EVENT_NETWORK_CLIENT_TOO_OLD = 211; + /** The configuration for a network has been requested (no action needed) */ + public static int ZTS_EVENT_NETWORK_REQ_CONFIG = 212; + /** The node joined the network successfully (no action needed) */ + public static int ZTS_EVENT_NETWORK_OK = 213; + /** The node is not allowed to join the network (you must authorize node) */ + public static int ZTS_EVENT_NETWORK_ACCESS_DENIED = 214; + /** The node has received an IPv4 address from the network controller */ + public static int ZTS_EVENT_NETWORK_READY_IP4 = 215; + /** The node has received an IPv6 address from the network controller */ + public static int ZTS_EVENT_NETWORK_READY_IP6 = 216; + /** Deprecated */ + public static int ZTS_EVENT_NETWORK_READY_IP4_IP6 = 217; + /** Network controller is unreachable */ + public static int ZTS_EVENT_NETWORK_DOWN = 218; + /** Network change received from controller */ + public static int ZTS_EVENT_NETWORK_UPDATE = 219; + + /** TCP/IP stack (lwIP) is up (for debug purposes) */ + public static int ZTS_EVENT_STACK_UP = 220; + /** TCP/IP stack (lwIP) id down (for debug purposes) */ + public static int ZTS_EVENT_STACK_DOWN = 221; + + /** lwIP netif up (for debug purposes) */ + public static int ZTS_EVENT_NETIF_UP = 230; + /** lwIP netif down (for debug purposes) */ + public static int ZTS_EVENT_NETIF_DOWN = 231; + /** lwIP netif removed (for debug purposes) */ + public static int ZTS_EVENT_NETIF_REMOVED = 232; + /** lwIP netif link up (for debug purposes) */ + public static int ZTS_EVENT_NETIF_LINK_UP = 233; + /** lwIP netif link down (for debug purposes) */ + public static int ZTS_EVENT_NETIF_LINK_DOWN = 234; + + /** A direct P2P path to peer is known */ + public static int ZTS_EVENT_PEER_DIRECT = 240; + /** A direct P2P path to peer is NOT known. Traffic is now relayed */ + public static int ZTS_EVENT_PEER_RELAY = 241; + /** A peer is unreachable. Check NAT/Firewall settings */ + public static int ZTS_EVENT_PEER_UNREACHABLE = 242; + /** A new path to a peer was discovered */ + public static int ZTS_EVENT_PEER_PATH_DISCOVERED = 243; + /** A known path to a peer is now considered dead */ + public static int ZTS_EVENT_PEER_PATH_DEAD = 244; + + /** A new managed network route was added */ + public static int ZTS_EVENT_ROUTE_ADDED = 250; + /** A managed network route was removed */ + public static int ZTS_EVENT_ROUTE_REMOVED = 251; + + /** A new managed IPv4 address was assigned to this peer */ + public static int ZTS_EVENT_ADDR_ADDED_IP4 = 260; + /** A managed IPv4 address assignment was removed from this peer */ + public static int ZTS_EVENT_ADDR_REMOVED_IP4 = 261; + /** A new managed IPv4 address was assigned to this peer */ + public static int ZTS_EVENT_ADDR_ADDED_IP6 = 262; + /** A managed IPv6 address assignment was removed from this peer */ + public static int ZTS_EVENT_ADDR_REMOVED_IP6 = 263; + + /** The node's secret key (identity) */ + public static int ZTS_EVENT_STORE_IDENTITY_SECRET = 270; + /** The node's public key (identity) */ + public static int ZTS_EVENT_STORE_IDENTITY_PUBLIC = 271; + /** The node has received an updated planet config */ + public static int ZTS_EVENT_STORE_PLANET = 272; + /** New reachability hints and peer configuration */ + public static int ZTS_EVENT_STORE_PEER = 273; + /** New network config */ + public static int ZTS_EVENT_STORE_NETWORK = 274; + + // Socket protocol types + /** Stream socket */ + public static int ZTS_SOCK_STREAM = 0x00000001; + /** Datagram socket */ + public static int ZTS_SOCK_DGRAM = 0x00000002; + public static int ZTS_SOCK_RAW = 0x00000003; + // Socket family types + /** IPv4 address family */ + public static int ZTS_AF_INET = 0x00000002; + // another test comment + /** IPv6 address family */ + public static int ZTS_AF_INET6 = 0x0000000a; + /* yet one more */ + public static int ZTS_PF_INET = ZTS_AF_INET; + public static int ZTS_PF_INET6 = ZTS_AF_INET6; + // Used as level numbers for setsockopt() and getsockopt() + public static int ZTS_IPPROTO_IP = 0x00000000; + public static int ZTS_IPPROTO_ICMP = 0x00000001; + public static int ZTS_IPPROTO_TCP = 0x00000006; + public static int ZTS_IPPROTO_UDP = 0x00000011; + public static int ZTS_IPPROTO_IPV6 = 0x00000029; + public static int ZTS_IPPROTO_ICMPV6 = 0x0000003a; + public static int ZTS_IPPROTO_UDPLITE = 0x00000088; + public static int ZTS_IPPROTO_RAW = 0x000000ff; + // send() and recv() flags + public static int ZTS_MSG_PEEK = 0x00000001; + public static int ZTS_MSG_WAITALL = 0x00000002; + public static int ZTS_MSG_OOB = 0x00000004; + public static int ZTS_MSG_DONTWAIT = 0x00000008; + public static int ZTS_MSG_MORE = 0x00000010; + // fnctl() commands + public static int ZTS_F_GETFL = 0x00000003; + public static int ZTS_F_SETFL = 0x00000004; + // fnctl() flags + public static int ZTS_O_NONBLOCK = 0x00000001; + public static int ZTS_O_NDELAY = 0x00000001; + // Shutdown commands + public static int ZTS_SHUT_RD = 0x00000000; + public static int ZTS_SHUT_WR = 0x00000001; + public static int ZTS_SHUT_RDWR = 0x00000002; + // ioctl() commands + public static int ZTS_FIONREAD = 0x4008667F; + public static int ZTS_FIONBIO = 0x8008667E; + // Socket level option number + public static int ZTS_SOL_SOCKET = 0x00000FFF; + // Socket options + public static int ZTS_SO_REUSEADDR = 0x00000004; + public static int ZTS_SO_KEEPALIVE = 0x00000008; + public static int ZTS_SO_BROADCAST = 0x00000020; + // Socket options + public static int ZTS_SO_DEBUG = 0x00000001; // NOT YET SUPPORTED + public static int ZTS_SO_ACCEPTCONN = 0x00000002; + public static int ZTS_SO_DONTROUTE = 0x00000010; // NOT YET SUPPORTED + public static int ZTS_SO_USELOOPBACK = 0x00000040; // NOT YET SUPPORTED + public static int ZTS_SO_LINGER = 0x00000080; + public static int ZTS_SO_DONTLINGER = ((int)(~ZTS_SO_LINGER)); + public static int ZTS_SO_OOBINLINE = 0x00000100; // NOT YET SUPPORTED + public static int ZTS_SO_REUSEPORT = 0x00000200; // NOT YET SUPPORTED + public static int ZTS_SO_SNDBUF = 0x00001001; // NOT YET SUPPORTED + public static int ZTS_SO_RCVBUF = 0x00001002; + public static int ZTS_SO_SNDLOWAT = 0x00001003; // NOT YET SUPPORTED + public static int ZTS_SO_RCVLOWAT = 0x00001004; // NOT YET SUPPORTED + public static int ZTS_SO_SNDTIMEO = 0x00001005; + public static int ZTS_SO_RCVTIMEO = 0x00001006; + public static int ZTS_SO_ERROR = 0x00001007; + public static int ZTS_SO_TYPE = 0x00001008; + public static int ZTS_SO_CONTIMEO = 0x00001009; // NOT YET SUPPORTED + public static int ZTS_SO_NO_CHECK = 0x0000100a; + // IPPROTO_IP options + public static int ZTS_IP_TOS = 0x00000001; + public static int ZTS_IP_TTL = 0x00000002; + // IPPROTO_TCP options + public static int ZTS_TCP_NODELAY = 0x00000001; + public static int ZTS_TCP_KEEPALIVE = 0x00000002; + public static int ZTS_TCP_KEEPIDLE = 0x00000003; + public static int ZTS_TCP_KEEPINTVL = 0x00000004; + public static int ZTS_TCP_KEEPCNT = 0x00000005; + + //----------------------------------------------------------------------------// + // ZeroTier Service Controls // + //----------------------------------------------------------------------------// + + //----------------------------------------------------------------------------// + // Error codes // + //----------------------------------------------------------------------------// + + /** (0) No error */ + public static int ZTS_ERR_OK = 0; + /** (-1) Socket error, see zts_errno */ + public static int ZTS_ERR_SOCKET = -1; + /** (-2) You probably did something at the wrong time */ + public static int ZTS_ERR_SERVICE = -2; + /** (-3) Invalid argument */ + public static int ZTS_ERR_ARG = -3; + /** (-4) No result (not necessarily an error) */ + public static int ZTS_ERR_RESULT = -4; + /** (-5) Consider filing a bug report */ + public static int ZTS_ERR_GENERAL = -5; + + //----------------------------------------------------------------------------// + // zts_errno Error codes // + //----------------------------------------------------------------------------// + + /** Operation not permitted (`zts_errno` value) */ + public static int ZTS_EPERM = 1; + /** No such file or directory */ + public static int ZTS_ENOENT = 2; + /** No such process */ + public static int ZTS_ESRCH = 3; + /** Interrupted system call */ + public static int ZTS_EINTR = 4; + /** I/O error */ + public static int ZTS_EIO = 5; + /** No such device or address */ + public static int ZTS_ENXIO = 6; + /** Bad file number */ + public static int ZTS_EBADF = 9; + /** Try again */ + public static int ZTS_EAGAIN = 11; + /** Operation would block */ + public static int ZTS_EWOULDBLOCK = 11; + /** Out of memory */ + public static int ZTS_ENOMEM = 12; + /** Permission denied */ + public static int ZTS_EACCES = 13; + /** Bad address */ + public static int ZTS_EFAULT = 14; + /** Device or resource busy */ + public static int ZTS_EBUSY = 16; + /** File exists */ + public static int ZTS_EEXIST = 17; + /** No such device */ + public static int ZTS_ENODEV = 19; + /** Invalid argument */ + public static int ZTS_EINVAL = 22; + /** File table overflow */ + public static int ZTS_ENFILE = 23; + /** Too many open files */ + public static int ZTS_EMFILE = 24; + /** Function not implemented */ + public static int ZTS_ENOSYS = 38; + /** Socket operation on non-socket */ + public static int ZTS_ENOTSOCK = 88; + /** Destination address required */ + public static int ZTS_EDESTADDRREQ = 89; + /** Message too long */ + public static int ZTS_EMSGSIZE = 90; + /** Protocol wrong type for socket */ + public static int ZTS_EPROTOTYPE = 91; + /** Protocol not available */ + public static int ZTS_ENOPROTOOPT = 92; + /** Protocol not supported */ + public static int ZTS_EPROTONOSUPPORT = 93; + /** Socket type not supported */ + public static int ZTS_ESOCKTNOSUPPORT = 94; + /** Operation not supported on transport endpoint */ + public static int ZTS_EOPNOTSUPP = 95; + /** Protocol family not supported */ + public static int ZTS_EPFNOSUPPORT = 96; + /** Address family not supported by protocol */ + public static int ZTS_EAFNOSUPPORT = 97; + /** Address already in use */ + public static int ZTS_EADDRINUSE = 98; + /** Cannot assign requested address */ + public static int ZTS_EADDRNOTAVAIL = 99; + /** Network is down */ + public static int ZTS_ENETDOWN = 100; + /** Network is unreachable */ + public static int ZTS_ENETUNREACH = 101; + /** Software caused connection abort */ + public static int ZTS_ECONNABORTED = 103; + /** Connection reset by peer */ + public static int ZTS_ECONNRESET = 104; + /** No buffer space available */ + public static int ZTS_ENOBUFS = 105; + /** Transport endpoint is already connected */ + public static int ZTS_EISCONN = 106; + /** Transport endpoint is not connected */ + public static int ZTS_ENOTCONN = 107; + /** Connection timed out */ + public static int ZTS_ETIMEDOUT = 110; + /** No route to host */ + public static int ZTS_EHOSTUNREACH = 113; + /** Operation already in progress */ + public static int ZTS_EALREADY = 114; + /** Operation now in progress */ + public static int ZTS_EINPROGRESS = 115; + + //----------------------------------------------------------------------------// + // Misc definitions // + //----------------------------------------------------------------------------// + + /** + * Length of human-readable MAC address string + */ + public static int ZTS_MAC_ADDRSTRLEN = 18; + + /** + * Max length of human-readable IPv4 string + */ + public static int ZTS_INET_ADDRSTRLEN = 16; + + /** + * Max length of human-readable IPv6 string + */ + public static int ZTS_INET6_ADDRSTRLEN = 46; + + /** + * Maximum (and required) length of string buffers used to receive + * string-format IP addresses from the API. This is set to `ZTS_INET6_ADDRSTRLEN` + * to handle all cases: `ZTS_AF_INET` and `ZTS_AF_INET6` + */ + public static int ZTS_IP_MAX_STR_LEN = 46; + + /** + * Required buffer length to safely receive data store items + */ + public static int ZTS_STORE_DATA_LEN = 4096; + + /** + * Maximum length of network short name + */ + public static int ZTS_MAX_NETWORK_SHORT_NAME_LENGTH = 127; + + /** + * Maximum number of pushed routes on a network + */ + public static int ZTS_MAX_NETWORK_ROUTES = 32; + + /** + * Maximum number of statically assigned IP addresses per network endpoint + * using ZT address management (not DHCP) + */ + public static int ZTS_MAX_ASSIGNED_ADDRESSES = 16; + + /** + * Maximum number of direct network paths to a given peer + */ + public static int ZTS_MAX_PEER_NETWORK_PATHS = 16; + + /** + * Maximum number of multicast groups a device / network interface can be + * subscribed to at once + */ + public static int ZTS_MAX_MULTICAST_SUBSCRIPTIONS = 1024; + + /** + * The length of a human-friendly identity key pair string + */ + public static int ZTS_ID_STR_BUF_LEN = 384; + + // public static native int zts_id_new(char* key, int* key_buf_len); + // public static native int zts_id_pair_is_valid(/*const*/ char* key, int len); + + public static native int zts_init_from_storage(String path); + public static native int zts_init_set_event_handler(ZeroTierEventListener callbackClass); + public static native int zts_init_set_port(short port); + + // public static native int zts_init_from_memory(/*const*/ char* key, int len); + public static native int zts_init_blacklist_if(/*const*/ String prefix, int len); + // public static native int zts_init_set_roots(/*const*/ void* roots_data, int len); + public static native int zts_init_allow_net_cache(int allowed); + public static native int zts_init_allow_peer_cache(int allowed); + public static native int zts_init_allow_roots_cache(int allowed); + public static native int zts_init_allow_id_cache(int allowed); + public static native int zts_addr_is_assigned(long net_id, int family); + // public static native int zts_addr_get(long net_id, int family, struct sockaddr_storage* addr); + // public static native int zts_addr_get_str(long net_id, int family, char* dst, int len); + // public static native int zts_addr_get_all(long net_id, struct sockaddr_storage* addr, int* count); + // public static native int zts_addr_compute_6plane(/*const*/ long net_id, /*const*/ long node_id, struct + // sockaddr_storage* addr); public static native int zts_addr_compute_rfc4193(/*const*/ long net_id, /*const*/ long + // node_id, struct sockaddr_storage* addr); + public static native int zts_addr_compute_rfc4193_str(long net_id, long node_id, String dst, int len); + public static native int zts_addr_compute_6plane_str(long net_id, long node_id, String dst, int len); + + public static native long zts_net_compute_adhoc_id(short start_port, short end_port); + public static native int zts_net_join(long net_id); + public static native int zts_net_leave(long net_id); + public static native int zts_net_transport_is_ready(/*const*/ long net_id); + public static native long zts_net_get_mac(long net_id); + public static native int zts_net_get_mac_str(long net_id, String dst, int len); + public static native int zts_net_get_broadcast(long net_id); + public static native int zts_net_get_mtu(long net_id); + public static native int zts_net_get_name(long net_id, String dst, int len); + public static native int zts_net_get_status(long net_id); + public static native int zts_net_get_type(long net_id); + + public static native int zts_route_is_assigned(long net_id, int family); + + public static native int zts_node_start(); + public static native int zts_node_is_online(); + public static native long zts_node_get_id(); + // public static native int zts_node_get_id_pair(char* key, int* key_buf_len); + public static native int zts_node_get_port(); + public static native int zts_node_stop(); + public static native int zts_node_free(); + + public static native int zts_moon_orbit(long moon_roots_id, long moon_seed); + public static native int zts_moon_deorbit(long moon_roots_id); + + // public static native int zts_socket(int family, int type, int protocol); + // public static native int zts_connect(int fd, /*const*/ struct sockaddr* addr, socklen_t addrlen); + // public static native int zts_bind(int fd, /*const*/ struct sockaddr* addr, socklen_t addrlen); + // public static native int zts_listen(int fd, int backlog); + // public static native int zts_accept(int fd, struct sockaddr* addr, socklen_t* addrlen); + // public static native int zts_setsockopt(int fd, int level, int optname, /*const*/ void* optval, socklen_t + // optlen); public static native int zts_getsockopt(int fd, int level, int optname, void* optval, socklen_t* + // optlen); public static native int zts_getsockname(int fd, struct sockaddr* addr, socklen_t* addrlen); public + // static native int zts_getpeername(int fd, struct sockaddr* addr, socklen_t* addrlen); public static native int + // zts_close(int fd); public static native int zts_select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* + // exceptfds, struct timeval* timeout); public static native int zts_fcntl(int fd, int cmd, int flags); public + // static native int zts_poll(struct pollfd* fds, nfds_t nfds, int timeout); public static native int zts_ioctl(int + // fd, long request, void* argp); public static native int send(int fd, /*const*/ void* buf, size_t len, int + // flags); public static native int sendto(int fd, + // /*const*/ void* buf, size_t len, int flags, /*const*/ struct sockaddr* addr, socklen_t addrlen); public static + // native int sendmsg(int fd, /*const*/ struct msghdr* msg, int flags); public static native int recv(int fd, + // void* buf, size_t len, int flags); public static native int recvfrom(int fd, void* buf, size_t len, int flags, + // struct sockaddr* addr, socklen_t* addrlen); public static native int recvmsg(int fd, struct msghdr* msg, int + // flags); public static native int read(int fd, void* buf, size_t len); public static native int readv(int fd, + // /*const*/ struct iovec* iov, int iovcnt); public static native int write(int fd, /*const*/ void* buf, size_t + // len); public static native int writev(int fd, /*const*/ struct iovec* iov, int iovcnt); public static native int + // shutdown(int fd, int how); + public static native int zts_simple_connect(int fd, /*const*/ String ipstr, int port, int timeout_ms); + public static native int zts_simple_bind(int fd, /*const*/ String ipstr, int port); + // public static native int zts_simple_accept(int fd, String remote_addr, int len, int* port); + public static native int zts_simple_tcp_client(/*const*/ String remote_ipstr, int remote_port); + // public static native int zts_simple_tcp_server(/*const*/ String local_ipstr, int local_port, String remote_ipstr, + // int len, int* remote_port); + public static native int zts_simple_udp_server(/*const*/ String local_ipstr, int local_port); + public static native int zts_simple_udp_client(/*const*/ String remote_ipstr); + public static native int zts_simple_set_no_delay(int fd, int enabled); + public static native int zts_simple_get_no_delay(int fd); + public static native int zts_simple_set_linger(int fd, int enabled, int value); + public static native int zts_simple_get_linger_enabled(int fd); + public static native int zts_simple_get_linger_value(int fd); + public static native int zts_simple_get_pending_data_size(int fd); + public static native int zts_simple_set_reuse_addr(int fd, int enabled); + public static native int zts_simple_get_reuse_addr(int fd); + public static native int zts_simple_set_recv_timeout(int fd, int seconds, int microseconds); + public static native int zts_simple_get_recv_timeout(int fd); + public static native int zts_simple_set_send_timeout(int fd, int seconds, int microseconds); + public static native int zts_simple_get_send_timeout(int fd); + public static native int zts_simple_set_send_buf_size(int fd, int size); + public static native int zts_simple_get_send_buf_size(int fd); + public static native int zts_simple_set_recv_buf_size(int fd, int size); + public static native int zts_simple_get_recv_buf_size(int fd); + public static native int zts_simple_set_ttl(int fd, int ttl); + public static native int zts_simple_get_ttl(int fd); + public static native int zts_simple_set_blocking(int fd, int enabled); + public static native int zts_simple_get_blocking(int fd); + public static native int zts_simple_set_keepalive(int fd, int enabled); + public static native int zts_simple_get_keepalive(int fd); + // struct hostent* gethostbyname(/*const*/ String name); + // public static native int zts_dns_set_server(uint8_t index, /*const*/ ip_addr* addr); + // ZTS_API /*const*/ ip_addr* ZTCALL dns_get_server(uint8_t index); + public static native int zts_core_lock_obtain(); + public static native int zts_core_lock_release(); + public static native int zts_core_query_addr_count(long net_id); + public static native int zts_core_query_addr(long net_id, int idx, String addr, int len); + public static native int zts_core_query_route_count(long net_id); + /* + public static native int zts_core_query_route( + long net_id, + int idx, + String target, + String via, + int len, + short* flags, + short* metric); + */ + public static native int zts_core_query_path_count(long peer_id); + public static native int zts_core_query_path(long peer_id, int idx, String dst, int len); + public static native int zts_core_query_mc_count(long net_id); + // public static native int zts_core_query_mc(long net_id, int idx, long* mac, uint32_t* adi); + /* + public static native int zts_util_roots_new( + char* roots_out, + int* roots_len, + char* prev_key, + int* prev_key_len, + char* curr_key, + int* curr_key_len, + long id, + long ts, + roots_t* roots_spec); + */ + public static native void zts_util_delay(long milliseconds); + + ////////////////////////////////////////////////////////////////////////////// + // Socket API // + ////////////////////////////////////////////////////////////////////////////// + + public static native int zts_socket(int family, int type, int protocol); + // public static native int zts_connect(int fd, ZeroTierSocketAddress addr); + // public static native int zts_bind(int fd, ZeroTierSocketAddress addr); + public static native int zts_listen(int fd, int backlog); + public static native int zts_accept(int fd, ZeroTierSocketAddress addr); + + // public static native int zts_setsockopt(int fd, int level, int optname, ZeroTierSocketOptionValue optval); + // public static native int zts_getsockopt(int fd, int level, int optname, ZeroTierSocketOptionValue optval); + + public static native int zts_read(int fd, byte[] buf); + public static native int zts_read_offset(int fd, byte[] buf, int offset, int len); + public static native int zts_read_length(int fd, byte[] buf, int len); + public static native int zts_recv(int fd, byte[] buf, int flags); + public static native int zts_recvfrom(int fd, byte[] buf, int flags, ZeroTierSocketAddress addr); + + public static native int zts_write(int fd, byte[] buf); + public static native int zts_write_byte(int fd, byte b); + public static native int zts_write_offset(int fd, byte[] buf, int offset, int len); + public static native int zts_sendto(int fd, byte[] buf, int flags, ZeroTierSocketAddress addr); + public static native int zts_send(int fd, byte[] buf, int flags); + + public static native int zts_shutdown(int fd, int how); + public static native int zts_close(int fd); + + public static native boolean zts_getsockname(int fd, ZeroTierSocketAddress addr); + public static native int zts_getpeername(int fd, ZeroTierSocketAddress addr); + public static native int zts_fcntl(int sock, int cmd, int flag); + // public static native int zts_ioctl(int fd, long request, ZeroTierIoctlArg arg); + public static native int zts_select( + int nfds, + ZeroTierFileDescriptorSet readfds, + ZeroTierFileDescriptorSet writefds, + ZeroTierFileDescriptorSet exceptfds, + int timeout_sec, + int timeout_usec); +} diff --git a/src/bindings/java/ZeroTierNode.java b/src/bindings/java/ZeroTierNode.java new file mode 100644 index 0000000..8fa72d9 --- /dev/null +++ b/src/bindings/java/ZeroTierNode.java @@ -0,0 +1,194 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package com.zerotier.sdk; + +import com.zerotier.sdk.*; + +/** + * Class that provides a control interface for nodes and networks by + * wrapping the ZeroTierNative class. + */ +public class ZeroTierNode { + /** + * Start the ZeroTier node + * + * @return Standard + */ + public int start() + { + return ZeroTierNative.zts_node_start(); + } + + /** + * Stop the ZeroTier node + * + * @return return + */ + public int stop() + { + return ZeroTierNative.zts_node_stop(); + } + + /** + * (Optional) Instruct ZeroTier to read and write identities and cache from a + * storage location. Note that this is an initialization method that should + * be called before {@code start()}. + * + * @param storagePath Where to read and write files + * + * @return return + */ + public int initFromStorage(String storagePath) + { + return ZeroTierNative.zts_init_from_storage(storagePath); + } + + /** + * (Optional) Whether caching of peer address information to storage is allowed. This + * is true by default if {@code initFromStorage()} is used. Note that this is an + * initialization method that can only be called before {@code start()}. + * + * @param allowed Whether caching or storage of this item is allowed + * + * @return return + */ + public int initAllowPeerCache(boolean allowed) + { + return ZeroTierNative.zts_init_allow_peer_cache(allowed ? 1 : 0); + } + + /** + * (Optional) Whether caching of network configuration data to storage is allowed. This + * is true by default if {@code initFromStorage()} is used. Note that this is an + * initialization method that can only be called before {@code start()}. + * + * @param allowed Whether caching or storage of this item is allowed + * + * @return return + */ + public int initAllowNetworkCache(boolean allowed) + { + return ZeroTierNative.zts_init_allow_net_cache(allowed ? 1 : 0); + } + + /** + * (Optional) Whether caching of identity key pairs to storage is allowed. This + * is true by default if {@code initFromStorage()} is used. Note that this is an + * initialization method that can only be called before {@code start()}. + * + * @param allowed Whether caching or storage of this item is allowed + * + * @return return + */ + public int initAllowIdCache(boolean allowed) + { + return ZeroTierNative.zts_init_allow_id_cache(allowed ? 1 : 0); + } + + /** + * (Optional) Whether caching of root topology to storage is allowed. This + * is true by default if {@code initFromStorage()} is used. Note that this is an + * initialization method that can only be called before {@code start()}. + * + * @param allowed Whether caching or storage of this item is allowed + * + * @return return + */ + public int initAllowRootsCache(boolean allowed) + { + return ZeroTierNative.zts_init_allow_roots_cache(allowed ? 1 : 0); + } + + /** + * (Optional) Set the ZeroTier service port. Note that this is an + * initialization method that can only be called before {@code start()}. + * + * @param port Port number + * + * @return return + */ + public int initSetPort(short port) + { + return ZeroTierNative.zts_init_set_port(port); + } + + /** + * (Optional) Set the event handler function. Note that this is an + * initialization method that can only be called before {@code start()}. + * + * @param handler Function to handle internal ZeroTier events + * + * @return return + */ + public int initSetEventHandler(ZeroTierEventListener handler) + { + return ZeroTierNative.zts_init_set_event_handler(handler); + } + + /** + * Return whether the ZeroTier node is currently online (able to reach a root) + * + * @return return + */ + public boolean isOnline() + { + return ZeroTierNative.zts_node_is_online() == 1; + } + + /** + * Join a network + * + * @param networkId Network to join + * + * @return return + */ + public int join(long networkId) + { + return ZeroTierNative.zts_net_join(networkId); + } + + /** + * Leave a network + * + * @param networkId Network to leave + * + * @return return + */ + public int leave(long networkId) + { + return ZeroTierNative.zts_net_leave(networkId); + } + + /** + * Return whether the given network is ready to process traffic + * + * @param networkId Network to test + * + * @return return + */ + public boolean isNetworkTransportReady(long networkId) + { + return ZeroTierNative.zts_net_transport_is_ready(networkId) == 1; + } + + /** + * Get the identity of this node (public key) + * + * @return 64-bit integer representing the 10-digit hexadecimal node ID + */ + public long getId() + { + return ZeroTierNative.zts_node_get_id(); + } +} diff --git a/src/bindings/java/ZeroTierOutputStream.java b/src/bindings/java/ZeroTierOutputStream.java new file mode 100644 index 0000000..8cbff7f --- /dev/null +++ b/src/bindings/java/ZeroTierOutputStream.java @@ -0,0 +1,97 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package com.zerotier.sdk; + +import com.zerotier.sdk.*; +import java.io.*; +import java.util.Arrays; +import java.util.Objects; + +/** + * Extends OutputStream using ZeroTier as a transport + */ +public class ZeroTierOutputStream extends OutputStream { + /** + * File descriptor used by lower native layer + */ + public int zfd = -1; + + /** + * Close the stream + * @exception IOException when an I/O error occurs + */ + public void close() throws IOException + { + /* Note: this operation currently only stops RX on a socket that is + shared between both I/OStreams. This means that closing this stream + will only shutdown that aspect of the socket but not actually close + it and free resources. Resources will be properly freed when the + socket implementation's native close() is called or if both I/OStreams + are closed separately */ + ZeroTierNative.zts_shutdown(zfd, ZeroTierNative.ZTS_SHUT_WR); + zfd = -1; + } + + /** + * Write a buffer + * @param originBuffer Source buffer + * @exception IOException when an I/O error occurs + */ + public void write(byte[] originBuffer) throws IOException + { + int bytesWritten = ZeroTierNative.zts_write(zfd, originBuffer); + if (bytesWritten < 0) { + throw new IOException("write(originBuffer[]), errno=" + bytesWritten); + } + } + + /** + * Write a buffer at offset + * @param originBuffer Source buffer + * @param offset Where in the buffer to start + * @param numBytes Number of bytes to write + * @exception IOException when an I/O error occurs + */ + public void write(byte[] originBuffer, int offset, int numBytes) throws IOException + { + Objects.requireNonNull(originBuffer, "input byte array (originBuffer) must not be null"); + if (offset < 0) { + throw new IndexOutOfBoundsException("offset < 0"); + } + if (numBytes < 0) { + throw new IndexOutOfBoundsException("numBytes < 0"); + } + if ((offset + numBytes) > originBuffer.length) { + throw new IndexOutOfBoundsException("(offset+numBytes) > originBuffer.length"); + } + int bytesWritten = ZeroTierNative.zts_write_offset(zfd, originBuffer, offset, numBytes); + if (bytesWritten < 0) { + throw new IOException("write(originBuffer[],offset,numBytes), errno=" + bytesWritten); + } + } + + /** + * Write low byte + * @param d Integer containing low byte to write + * @exception IOException when an I/O error occurs + */ + public void write(int d) throws IOException + { + byte lowByte = (byte)(d & 0xFF); + int bytesWritten = ZeroTierNative.zts_write_byte(zfd, lowByte); + if (bytesWritten < 0) { + throw new IOException("write(d), errno=" + bytesWritten); + } + } +} diff --git a/src/bindings/java/ZeroTierPeerDetails.java b/src/bindings/java/ZeroTierPeerDetails.java new file mode 100644 index 0000000..fd83f77 --- /dev/null +++ b/src/bindings/java/ZeroTierPeerDetails.java @@ -0,0 +1,61 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package com.zerotier.sdk; + +import com.zerotier.sdk.*; + +/** + * This class encapsulates details about a Peer on a ZeroTier network + */ +class ZeroTierPeerDetails { + /** + * ZeroTier address (40 bits) + */ + public long address; + + /** + * Remote major version or -1 if not known + */ + public int versionMajor; + + /** + * Remote minor version or -1 if not known + */ + public int versionMinor; + + /** + * Remote revision or -1 if not known + */ + public int versionRev; + + /** + * Last measured latency in milliseconds or -1 if unknown + */ + public int latency; + + /** + * What trust hierarchy role does this device have? + */ + public int role; + + /** + * Number of paths (size of paths[]) + */ + public int pathCount; + + /** + * Known network paths to peer + */ + public ZeroTierSocketAddress[] paths = new ZeroTierSocketAddress[ZeroTierNative.ZTS_MAX_PEER_NETWORK_PATHS]; +} diff --git a/src/bindings/java/ZeroTierSocket.java b/src/bindings/java/ZeroTierSocket.java new file mode 100644 index 0000000..4aafb75 --- /dev/null +++ b/src/bindings/java/ZeroTierSocket.java @@ -0,0 +1,617 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package com.zerotier.sdk; + +import com.zerotier.sdk.*; +import java.io.*; +import java.net.*; + +/** + * Implements Socket-like behavior over ZeroTier + * + * @author ZeroTier, Inc. + */ +public class ZeroTierSocket { + // File descriptor from lower native layer + private int _zfd = -1; + private int _family = -1; + private int _type = -1; + private int _protocol = -1; + + // State flags + private boolean _isClosed = false; + private boolean _isConnected = false; + private boolean _isBound = false; + private boolean _inputHasBeenShutdown = false; + private boolean _outputHasBeenShutdown = false; + + // Input and Output streams + private ZeroTierInputStream _inputStream = new ZeroTierInputStream(); + private ZeroTierOutputStream _outputStream = new ZeroTierOutputStream(); + + // The remote address to which the ZeroTierSocket is connected + private InetAddress _remoteAddr; + private int _remotePort; + private InetAddress _localAddr; + private int _localPort; + + private void setNativeFileDescriptor(int fd) + { + _zfd = fd; + _inputStream.zfd = fd; + _outputStream.zfd = fd; + } + + private ZeroTierSocket(int family, int type, int protocol, int zfd) + { + _family = family; + _type = type; + _protocol = protocol; + setNativeFileDescriptor(zfd); + // Since we only call this from accept() we will mark it as connected + _isConnected = true; + } + + /** + * Create a new ZeroTierSocket with the given attributes + * @param family The socket family + * @param type The socket type + * @param protocol Supported protocol + * + * @exception IOException when an I/O error occurs + */ + public ZeroTierSocket(int family, int type, int protocol) throws IOException + { + if (_zfd > -1) { + throw new IOException("This socket has already been created (fd=" + _zfd + ")"); + } + _zfd = ZeroTierNative.zts_socket(ZeroTierNative.ZTS_AF_INET, ZeroTierNative.ZTS_SOCK_STREAM, protocol); + if (_zfd < 0) { + throw new IOException("Error while creating socket (" + _zfd + ")"); + } + _family = family; + _type = type; + _protocol = protocol; + setNativeFileDescriptor(_zfd); + } + + /** + * Connect to a remote host + * @param remoteAddr Remote address to which this socket should connect + * @param remotePort Remote port to which this socket should connect + * + * @exception IOException when an I/O error occurs + */ + public void connect(InetAddress remoteAddr, int remotePort) throws IOException + { + if (_zfd < 0) { + throw new IOException("Invalid socket (fd < 0)"); + } + if ((remoteAddr instanceof Inet4Address) && _family != ZeroTierNative.ZTS_AF_INET) { + throw new IOException("Invalid address type. Socket is of type AF_INET"); + } + if ((remoteAddr instanceof Inet6Address) && _family != ZeroTierNative.ZTS_AF_INET6) { + throw new IOException("Invalid address type. Socket is of type AF_INET6"); + } + int err; + if ((err = ZeroTierNative.zts_simple_connect(_zfd, remoteAddr.getHostAddress(), remotePort, 0)) < 0) { + throw new IOException("Error while connecting to remote host (" + err + ")"); + } + _isConnected = true; + } + + /** + * Connect to a remote host + * @param remoteAddr Remote address to which this socket should connect + * @param remotePort Remote port to which this socket should connect + * + * @exception IOException when an I/O error occurs + */ + public void connect(String remoteAddr, int remotePort) throws IOException + { + InetAddress remoteInetAddr = InetAddress.getByName(remoteAddr); + connect(remoteInetAddr, remotePort); + } + + /** + * Connect to a remote host + * @param remoteAddr Remote address to which this socket should connect + * + * @exception IOException when an I/O error occurs + */ + public void connect(SocketAddress remoteAddr) throws IOException + { + int remotePort = ((InetSocketAddress)remoteAddr).getPort(); + connect(((InetSocketAddress)remoteAddr).getHostString(), remotePort); + } + + /** + * Bind to a local address + * @param localAddr Local address to which this socket should bind + * @param localPort Local port to which this socket should bind + * + * @exception IOException when an I/O error occurs + */ + public void bind(InetAddress localAddr, int localPort) throws IOException + { + if (_zfd < 0) { + throw new IOException("Invalid socket (fd < 0)"); + } + if ((localAddr instanceof Inet4Address) && _family != ZeroTierNative.ZTS_AF_INET) { + throw new IOException("Invalid address type. Socket is of type AF_INET"); + } + if ((localAddr instanceof Inet6Address) && _family != ZeroTierNative.ZTS_AF_INET6) { + throw new IOException("Invalid address type. Socket is of type AF_INET6"); + } + int err; + if ((err = ZeroTierNative.zts_simple_bind(_zfd, localAddr.getHostAddress(), localPort)) < 0) { + throw new IOException("Error while connecting to remote host (" + err + ")"); + } + _localPort = localPort; + _isBound = true; + } + + /** + * Bind to a local address + * @param localAddr Local address to which this socket should bind + * @param localPort Local port to which this socket should bind + * + * @exception IOException when an I/O error occurs + */ + public void bind(String localAddr, int localPort) throws IOException + { + InetAddress localInetAddr = InetAddress.getByName(localAddr); + bind(localInetAddr, localPort); + } + + /** + * Put the ZeroTierSocket into a listening state + * @param backlog Size of connection backlog + * + * @exception IOException when an I/O error occurs + */ + public void listen(int backlog) throws IOException + { + if (_zfd < 0) { + throw new IOException("Invalid socket (fd < 0)"); + } + if (backlog < 0) { + throw new IOException("Invalid backlog value"); + } + int err; + if ((err = ZeroTierNative.zts_listen(_zfd, backlog)) < 0) { + throw new IOException("Error while putting socket into listening state (" + err + ")"); + } + } + + /** + * Accept incoming connections on this ZeroTierSocket + * @return New ZeroTierSocket representing the accepted connection + * @exception IOException when an I/O error occurs + */ + public ZeroTierSocket accept() throws IOException + { + if (_zfd < 0) { + throw new IOException("Invalid socket (fd < 0)"); + } + int accetpedFd = -1; + ZeroTierSocketAddress addr = new ZeroTierSocketAddress(); + if ((accetpedFd = ZeroTierNative.zts_accept(_zfd, addr)) < 0) { + throw new IOException("Error while accepting connection (" + accetpedFd + ")"); + } + return new ZeroTierSocket(_family, _type, _protocol, accetpedFd); + } + + /** + * Close the ZeroTierSocket. + * + * @exception IOException when an I/O error occurs + */ + public void close() throws IOException + { + if (_zfd < 0) { + throw new IOException("Invalid socket (fd < 0)"); + } + ZeroTierNative.zts_close(_zfd); + _isClosed = true; + } + + /** + * Return whether keepalive is enabled. + * @return true or false + * @exception SocketException when an error occurs in the native socket layer + */ + public boolean getKeepAlive() throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + return ZeroTierNative.zts_simple_get_keepalive(_zfd) == 1; + } + + /** + * Get the local port to which this ZeroTierSocket is bound + * @return Local port + */ + public int getLocalPort() + { + if (! _isBound) { + return -1; + } + return _localPort; + } + + /** + * Get the local address to which this ZeroTierSocket is bound + * @return Local address + */ + public InetAddress getLocalAddress() + { + if (! _isBound) { + return null; + } + return _localAddr; + } + + /** + * Get the remote port to which this ZeroTierSocket is bound + * @return Remote port + */ + public int getRemotePort() + { + if (! _isConnected) { + return -1; + } + return _remotePort; + } + + /** + * Get the remote address to which this ZeroTierSocket is bound + * @return Remote address + */ + public InetAddress getRemoteAddress() + { + if (! _isConnected) { + return null; + } + return _remoteAddr; + } + + /** + * Return the size of the receive buffer for the ZeroTierSocket's ZeroTierInputStream (SO_RCVBUF) + * @return Size of the receive buffer + * @exception SocketException when an error occurs in the native socket layer + */ + public int getReceiveBufferSize() throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + return ZeroTierNative.zts_simple_get_recv_buf_size(_zfd); + } + + /** + * Return the size of the send buffer for the ZeroTierSocket's ZeroTierOutputStream (SO_SNDBUF) + * @return Size of the send buffer + * @exception SocketException when an error occurs in the native socket layer + */ + public int getSendBufferSize() throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + return ZeroTierNative.zts_simple_get_send_buf_size(_zfd); + } + + /** + * Return whether address reuse is enabled on this ZeroTierSocket (SO_REUSEADDR) + * @return true or false + * @exception SocketException when an error occurs in the native socket layer + */ + public boolean getReuseAddressEnabled() throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + return ZeroTierNative.zts_simple_get_reuse_addr(_zfd) == 1; + } + + /** + * Return the amount of time that a ZeroTierSocket will linger after closure (SO_LINGER) + * @return Nothing. + * @exception SocketException when an error occurs in the native socket layer + */ + public int getSoLingerTime() throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + return ZeroTierNative.zts_simple_get_linger_value(_zfd); + } + + /** + * Get the ZeroTierSocket's timeout value (SO_RCVTIMEO) + * @return Nothing. + * @exception SocketException when an error occurs in the native socket layer + */ + public int getSoTimeout() throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + return ZeroTierNative.zts_simple_get_recv_timeout(_zfd); + } + + /** + * Return whether TCP no-delay is enabled (TCP_NODELAY) + * @return true or false + * @exception SocketException when an error occurs in the native socket layer + */ + public boolean tcpNoDelayEnabled() throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + return ZeroTierNative.zts_simple_get_no_delay(_zfd) == 1; + } + + /** + * Return whether this ZeroTierSocket is bound to a local address + * @return true or false + */ + public boolean isBound​() + { + return _isBound; + } + + /** + * Return whether this ZeroTierSocket has been closed + * @return true or false + */ + public boolean isClosed​() + { + return _isClosed; + } + + /** + * Return whether this ZeroTierSocket is connected to a remote address + * @return true or false + */ + public boolean isConnected​() + { + return _isConnected; + } + + /** + * Disable the input-aspect of the ZeroTierSocket. + * + * @exception SocketException when an error occurs in the native socket layer + */ + public void shutdownInput() throws SocketException + { + if (! _isConnected) { + throw new SocketException("Error: ZeroTierSocket is not connected"); + } + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (_inputHasBeenShutdown) { + throw new SocketException("Error: ZeroTierSocket input has been shut down"); + } + ZeroTierNative.zts_shutdown(_zfd, ZeroTierNative.ZTS_SHUT_RD); + _inputHasBeenShutdown = true; + } + + /** + * Disable the output-aspect of the ZeroTierSocket. + * + * @exception SocketException when an error occurs in the native socket layer + */ + public void shutdownOutput() throws SocketException + { + if (! _isConnected) { + throw new SocketException("Error: ZeroTierSocket is not connected"); + } + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (_outputHasBeenShutdown) { + throw new SocketException("Error: ZeroTierSocket output has been shut down"); + } + ZeroTierNative.zts_shutdown(_zfd, ZeroTierNative.ZTS_SHUT_WR); + _outputHasBeenShutdown = true; + } + + /** + * Return a reference to the ZeroTierInputStream used by this ZeroTierSocket + * @return A reference to the ZeroTierInputStream + * @exception SocketException when an error occurs in the native socket layer + */ + public ZeroTierInputStream getInputStream() throws SocketException + { + if (! _isConnected) { + throw new SocketException("Error: ZeroTierSocket is not connected"); + } + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (_inputHasBeenShutdown) { + throw new SocketException("Error: ZeroTierSocket input has been shut down"); + } + return _inputStream; + } + + /** + * Return a reference to the ZeroTierOutputStream used by this ZeroTierSocket + * @return A reference to the ZeroTierOutputStream + * @exception SocketException when an error occurs in the native socket layer + */ + public ZeroTierOutputStream getOutputStream() throws SocketException + { + if (! _isConnected) { + throw new SocketException("Error: ZeroTierSocket is not connected"); + } + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (_outputHasBeenShutdown) { + throw new SocketException("Error: ZeroTierSocket output has been shut down"); + } + return _outputStream; + } + + /** + * Return whether the input-aspect of the ZeroTierSocket has been disabled. + * @return true or false + */ + public boolean inputStreamHasBeenShutdown​() + { + return _inputHasBeenShutdown; + } + + /** + * Return whether the output-aspect of the ZeroTierSocket has been disabled. + * @return true or false + */ + public boolean outputStreamHasBeenShutdown​() + { + return _outputHasBeenShutdown; + } + + /** + * Enable or disable the keepalive setting (SO_KEEPALIVE) + * @param enabled Whether SO_KEEPALIVE is enabled. + * + * @exception SocketException when an error occurs in the native socket layer + */ + public void setKeepAliveEnabled(boolean enabled) throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (ZeroTierNative.zts_simple_set_keepalive(_zfd, (enabled ? 1 : 0)) != ZeroTierNative.ZTS_ERR_OK) { + throw new SocketException("Error: Could not set SO_KEEPALIVE"); + } + } + + /** + * Set the size of the receive buffer for the ZeroTierSocket's ZeroTierInputStream. + * @param bufferSize Size of receive buffer + * + * @exception SocketException when an error occurs in the native socket layer + */ + public void setReceiveBufferSize(int bufferSize) throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (bufferSize <= 0) { + throw new IllegalArgumentException("Error: bufferSize <= 0"); + } + if (ZeroTierNative.zts_simple_set_recv_buf_size(_zfd, bufferSize) != ZeroTierNative.ZTS_ERR_OK) { + throw new SocketException("Error: Could not set receive buffer size"); + } + } + + /** + * Enable or disable the re-use of addresses (SO_REUSEADDR) + * @param enabled Whether SO_REUSEADDR is enabled + * + * @exception SocketException when an error occurs in the native socket layer + */ + public void setReuseAddress(boolean enabled) throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (ZeroTierNative.zts_simple_set_reuse_addr(_zfd, (enabled ? 1 : 0)) != ZeroTierNative.ZTS_ERR_OK) { + throw new SocketException("Error: Could not set SO_REUSEADDR"); + } + } + + /** + * Set the size of the send buffer for the ZeroTierSocket's ZeroTierOutputStream (SO_SNDBUF) + * @param bufferSize Size of send buffer + * + * @exception SocketException when an error occurs in the native socket layer + */ + public void setSendBufferSize(int bufferSize) throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (bufferSize <= 0) { + throw new IllegalArgumentException("Error: bufferSize <= 0"); + } + if (ZeroTierNative.zts_simple_set_send_buf_size(_zfd, bufferSize) != ZeroTierNative.ZTS_ERR_OK) { + throw new SocketException("Error: Could not set SO_SNDBUF"); + } + } + + /** + * Set the amount of time that a ZeroTierSocket will linger after closure (SO_LINGER) + * @param enabled Whether SO_LINGER is enabled + * @param lingerTime SO_LINGER time + * + * @exception SocketException when an error occurs in the native socket layer + */ + public void setSoLinger(boolean enabled, int lingerTime) throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (lingerTime < 0) { + throw new IllegalArgumentException("Error: lingerTime < 0"); + } + if (ZeroTierNative.zts_simple_set_linger(_zfd, (enabled ? 1 : 0), lingerTime) != ZeroTierNative.ZTS_ERR_OK) { + throw new SocketException("Error: Could not set ZTS_SO_LINGER"); + } + } + + /** + * Set the timeout value for SO_RCVTIMEO + * @param timeout Socket receive timeout value. + * + * @exception SocketException when an error occurs in the native socket layer + */ + public void setSoTimeoutValue(int timeout) throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (timeout < 0) { + throw new IllegalArgumentException("Error: SO_TIMEOUT < 0"); + } + // TODO: This is incorrect + if (ZeroTierNative.zts_simple_set_recv_timeout(_zfd, timeout, timeout) != ZeroTierNative.ZTS_ERR_OK) { + throw new SocketException("Error: Could not set SO_RCVTIMEO"); + } + } + + /** + * Enable or disable TCP_NODELAY + * @param enabled Whether TCP_NODELAY is enabled + * + * @exception SocketException when an error occurs in the native socket layer + */ + public void setTcpNoDelayEnabled(boolean enabled) throws SocketException + { + if (_isClosed) { + throw new SocketException("Error: ZeroTierSocket is closed"); + } + if (ZeroTierNative.zts_simple_set_no_delay(_zfd, (enabled ? 1 : 0)) != ZeroTierNative.ZTS_ERR_OK) { + throw new SocketException("Error: Could not set TCP_NODELAY"); + } + } +} diff --git a/src/bindings/java/ZeroTierSocketAddress.java b/src/bindings/java/ZeroTierSocketAddress.java new file mode 100644 index 0000000..433f219 --- /dev/null +++ b/src/bindings/java/ZeroTierSocketAddress.java @@ -0,0 +1,124 @@ +/* + * Copyright (c)2013-2021 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2026-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +package com.zerotier.sdk; + +import com.zerotier.sdk.ZeroTierNative; +import java.net.InetAddress; + +/** + * Convenience class for holding address information. Used internally by JNI layer. + */ +class ZeroTierSocketAddress { + private byte[] _ip6 = new byte[16]; + private byte[] _ip4 = new byte[4]; + + private int _family; + private int _port; // Also reused for netmask or prefix + + public ZeroTierSocketAddress() + { + } + + /** + * Constructor + */ + public ZeroTierSocketAddress(String ipStr, int port) + { + if (ipStr.contains(":")) { + _family = ZeroTierNative.ZTS_AF_INET6; + try { + InetAddress ip = InetAddress.getByName(ipStr); + _ip6 = ip.getAddress(); + } + catch (Exception e) { + } + } + else if (ipStr.contains(".")) { + _family = ZeroTierNative.ZTS_AF_INET; + try { + InetAddress ip = InetAddress.getByName(ipStr); + _ip4 = ip.getAddress(); + } + catch (Exception e) { + } + } + _port = port; + } + + /** + * Convert to string (ip portion only) + */ + public String ipString() + { + if (_family == ZeroTierNative.ZTS_AF_INET) { + try { + InetAddress inet = InetAddress.getByAddress(_ip4); + return "" + inet.getHostAddress(); + } + catch (Exception e) { + System.out.println(e); + } + } + if (_family == ZeroTierNative.ZTS_AF_INET6) { + try { + InetAddress inet = InetAddress.getByAddress(_ip6); + return "" + inet.getHostAddress(); + } + catch (Exception e) { + System.out.println(e); + } + } + return ""; + } + + /** + * Convert to string (ip and port) + */ + public String toString() + { + return ipString() + ":" + _port; + } + + /** + * Convert to string (ip+netmask and port) + */ + public String toCIDR() + { + return ipString() + "/" + _port; + } + + /** + * Get port + */ + public int getPort() + { + return _port; + } + + /** + * Get netmask + */ + public int getNetmask() + { + return _port; + } + + /** + * Get prefix (stored in port) + */ + public int getPrefix() + { + return _port; + } +} diff --git a/test/selftest.java b/test/selftest.java new file mode 100644 index 0000000..ba3d761 --- /dev/null +++ b/test/selftest.java @@ -0,0 +1,138 @@ +import com.zerotier.sdk.*; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.math.BigInteger; + +public class selftest { + public static void main(String[] args) + { + System.err.println(args.length); + if (args.length < 4 || args.length > 5) { + System.err.println("Invalid arguments"); + System.err.println(" Usage: "); + System.err.println(" example server ./ 0123456789abcdef 8080"); + System.err.println(" example client ./ 0123456789abcdef 192.168.22.1 8080\n"); + System.exit(1); + } + + String storagePath = ""; + String remoteAddr = ""; + int port = 0; + String mode = args[0]; + storagePath = args[1]; + BigInteger networkId = new BigInteger(args[2], 16); + if (args.length == 4) { + port = Integer.parseInt(args[3]); + } + if (args.length == 5) { + remoteAddr = args[3]; + port = Integer.parseInt(args[4]); + } + System.out.println("mode = " + mode); + System.out.println("networkId = " + Long.toHexString(networkId.longValue())); + System.out.println("storagePath = " + storagePath); + System.out.println("remoteAddr = " + remoteAddr); + System.out.println("port = " + port); + + // ZeroTier setup + + ZeroTierNode node = new ZeroTierNode(); + + node.initFromStorage(storagePath); + // node.initSetEventHandler(new MyZeroTierEventListener()); + // node.initSetPort(9994); + node.start(); + + System.out.println("Waiting for node to come online..."); + while (! node.isOnline()) { + ZeroTierNative.zts_util_delay(50); + } + System.out.println("Node ID: " + Long.toHexString(node.getId())); + System.out.println("Joining network..."); + node.join(networkId.longValue()); + System.out.println("Waiting for network..."); + while (! node.isNetworkTransportReady(networkId.longValue())) { + ZeroTierNative.zts_util_delay(50); + } + System.out.println("joined"); + + // Socket logic + + if (mode.equals("server")) { + System.out.println("Starting server..."); + try { + ZeroTierSocket socket = + new ZeroTierSocket(ZeroTierNative.ZTS_AF_INET, ZeroTierNative.ZTS_SOCK_STREAM, 0); + socket.bind("0.0.0.0", port); + socket.listen(100); + ZeroTierSocket newConnection = socket.accept(); + ZeroTierInputStream inputStream = newConnection.getInputStream(); + DataInputStream dataInputStream = new DataInputStream(inputStream); + String message = dataInputStream.readUTF(); + System.out.println("recv: " + message); + socket.close(); + newConnection.close(); + } + catch (Exception ex) { + System.out.println(ex); + } + } + + if (mode.equals("client")) { + System.out.println("Starting client..."); + try { + ZeroTierSocket socket = + new ZeroTierSocket(ZeroTierNative.ZTS_AF_INET, ZeroTierNative.ZTS_SOCK_STREAM, 0); + socket.connect(remoteAddr, port); + ZeroTierOutputStream outputStream = socket.getOutputStream(); + DataOutputStream dataOutputStream = new DataOutputStream(outputStream); + dataOutputStream.writeUTF("Hello from java!"); + socket.close(); + } + catch (Exception ex) { + System.out.println(ex); + } + } + } +} + +/** + * (OPTIONAL) event handler + */ +/* +class MyZeroTierEventListener implements ZeroTierEventListener { + public void onZeroTierEvent(long id, int event_code) + { + if (event_code == ZeroTierNative.ZTS_EVENT_NODE_UP) { + System.out.println("EVENT_NODE_UP"); + } + if (event_code == ZeroTierNative.ZTS_EVENT_NODE_ONLINE) { + System.out.println("EVENT_NODE_ONLINE: " + Long.toHexString(id)); + } + if (event_code == ZeroTierNative.ZTS_EVENT_NODE_OFFLINE) { + System.out.println("EVENT_NODE_OFFLINE"); + } + if (event_code == ZeroTierNative.ZTS_EVENT_NODE_DOWN) { + System.out.println("EVENT_NODE_DOWN"); + } + if (event_code == ZeroTierNative.ZTS_EVENT_NETWORK_READY_IP4) { + System.out.println("ZTS_EVENT_NETWORK_READY_IP4: " + Long.toHexString(id)); + } + if (event_code == ZeroTierNative.ZTS_EVENT_NETWORK_READY_IP6) { + System.out.println("ZTS_EVENT_NETWORK_READY_IP6: " + Long.toHexString(id)); + } + if (event_code == ZeroTierNative.ZTS_EVENT_NETWORK_DOWN) { + System.out.println("EVENT_NETWORK_DOWN: " + Long.toHexString(id)); + } + if (event_code == ZeroTierNative.ZTS_EVENT_NETWORK_OK) { + System.out.println("EVENT_NETWORK_OK: " + Long.toHexString(id)); + } + if (event_code == ZeroTierNative.ZTS_EVENT_NETWORK_ACCESS_DENIED) { + System.out.println("EVENT_NETWORK_ACCESS_DENIED: " + Long.toHexString(id)); + } + if (event_code == ZeroTierNative.ZTS_EVENT_NETWORK_NOT_FOUND) { + System.out.println("EVENT_NETWORK_NOT_FOUND: " + Long.toHexString(id)); + } + } +} +*/