summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/CMakeLists.txt90
-rw-r--r--java/README.md17
-rw-r--r--java/build.xml122
-rw-r--r--java/jni/Android.mk44
-rw-r--r--java/jni/Application.mk3
-rw-r--r--java/jni/ZT1_jnicache.cpp242
-rw-r--r--java/jni/ZT1_jnicache.h65
-rw-r--r--java/jni/ZT1_jniutils.cpp875
-rw-r--r--java/jni/ZT1_jniutils.h50
-rw-r--r--java/jni/com_zerotierone_sdk_Node.cpp1197
-rw-r--r--java/jni/com_zerotierone_sdk_Node.h133
-rw-r--r--java/src/com/zerotier/one/AndroidFileProvider.java43
-rw-r--r--java/src/com/zerotier/one/DataStore.java73
-rw-r--r--java/src/com/zerotier/one/DataStoreFileProvider.java12
-rw-r--r--java/src/com/zerotier/one/JavaFileProvider.java46
-rw-r--r--java/src/com/zerotier/one/OneService.java204
-rw-r--r--java/src/com/zerotier/sdk/DataStoreGetListener.java58
-rw-r--r--java/src/com/zerotier/sdk/DataStorePutListener.java59
-rw-r--r--java/src/com/zerotier/sdk/Event.java123
-rw-r--r--java/src/com/zerotier/sdk/EventListener.java67
-rw-r--r--java/src/com/zerotier/sdk/MulticastGroup.java49
-rw-r--r--java/src/com/zerotier/sdk/NativeUtils.java93
-rw-r--r--java/src/com/zerotier/sdk/Node.java432
-rw-r--r--java/src/com/zerotier/sdk/NodeException.java36
-rw-r--r--java/src/com/zerotier/sdk/NodeStatus.java69
-rw-r--r--java/src/com/zerotier/sdk/PacketSender.java47
-rw-r--r--java/src/com/zerotier/sdk/Peer.java110
-rw-r--r--java/src/com/zerotier/sdk/PeerPhysicalPath.java86
-rw-r--r--java/src/com/zerotier/sdk/PeerRole.java45
-rw-r--r--java/src/com/zerotier/sdk/ResultCode.java74
-rw-r--r--java/src/com/zerotier/sdk/Version.java37
-rw-r--r--java/src/com/zerotier/sdk/VirtualNetworkConfig.java170
-rw-r--r--java/src/com/zerotier/sdk/VirtualNetworkConfigListener.java60
-rw-r--r--java/src/com/zerotier/sdk/VirtualNetworkConfigOperation.java49
-rw-r--r--java/src/com/zerotier/sdk/VirtualNetworkFrameListener.java48
-rw-r--r--java/src/com/zerotier/sdk/VirtualNetworkStatus.java59
-rw-r--r--java/src/com/zerotier/sdk/VirtualNetworkType.java39
37 files changed, 5026 insertions, 0 deletions
diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt
new file mode 100644
index 00000000..db3eec1c
--- /dev/null
+++ b/java/CMakeLists.txt
@@ -0,0 +1,90 @@
+cmake_minimum_required(VERSION 3.2)
+
+project(ZeroTierOneJNI)
+
+find_package(Java COMPONENTS Development)
+message("JAVA_HOME: $ENV{JAVA_HOME}")
+
+if(WIN32)
+set(Java_INCLUDE_DIRS $ENV{JAVA_HOME}/include)
+endif()
+
+if(APPLE)
+set(Java_INCLUDE_DIRS "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks/JavaVM.framework/Headers")
+endif()
+
+message("Java Include Dirs: ${Java_INCLUDE_DIRS}")
+
+if(WIN32)
+ add_definitions(-DNOMINMAX)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /W3 /MP")
+endif()
+
+if(APPLE)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch i386 -arch x86_64 -Wall -O3 -flto -fPIE -fvectorize -fstack-protector -mmacosx-version-min=10.7 -Wno-unused-private-field")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS} -fno-rtti")
+endif()
+
+set(src_files
+ ../ext/lz4/lz4.c
+ ../ext/json-parser/json.c
+ ../ext/http-parser/http_parser.c
+ ../node/C25519.cpp
+ ../node/CertificateOfMembership.cpp
+ ../node/Defaults.cpp
+ ../node/Dictionary.cpp
+ ../node/Identity.cpp
+ ../node/IncomingPacket.cpp
+ ../node/InetAddress.cpp
+ ../node/Multicaster.cpp
+ ../node/Network.cpp
+ ../node/NetworkConfig.cpp
+ ../node/Node.cpp
+ ../node/OutboundMulticast.cpp
+ ../node/Packet.cpp
+ ../node/Peer.cpp
+ ../node/Poly1305.cpp
+ ../node/Salsa20.cpp
+ ../node/SelfAwareness.cpp
+ ../node/SHA512.cpp
+ ../node/Switch.cpp
+ ../node/Topology.cpp
+ ../node/Utils.cpp
+ ../osdep/Http.cpp
+ ../osdep/OSUtils.cpp
+ jni/com_zerotierone_sdk_Node.cpp
+ jni/ZT1_jniutils.cpp
+ jni/ZT1_jnicache.cpp
+ )
+
+set(include_dirs
+ ${CMAKE_CURRENT_SOURCE_DIR}/../include/
+ ${Java_INCLUDE_DIRS})
+
+if(WIN32)
+ set(include_dirs
+ ${include_dirs}
+ ${Java_INCLUDE_DIRS}/win32)
+endif()
+
+include_directories(
+ ${include_dirs}
+ )
+
+add_library(${PROJECT_NAME} SHARED ${src_files})
+
+if(APPLE)
+ set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".jnilib")
+endif()
+
+set(link_libs )
+
+if(WIN32)
+ set(link_libs
+ wsock32
+ ws2_32
+
+ )
+endif()
+
+target_link_libraries(${PROJECT_NAME} ${link_libs}) \ No newline at end of file
diff --git a/java/README.md b/java/README.md
new file mode 100644
index 00000000..2650ec3d
--- /dev/null
+++ b/java/README.md
@@ -0,0 +1,17 @@
+ZeroTier One SDK - Android JNI Wrapper
+=====
+
+
+Building
+-----
+
+Reqires:
+
+* JDK
+* ANT
+* Android NDK
+
+Required Environment Variables:
+
+* NDK\_BUILD\_LOC - Path do the ndk-build script in the Android NDK
+* ANDROID\_PLATFORM - path to the directory android.jar lives (on Windows: C:\Users\<username>\AppData\Local\Android\sdk\platforms\android-21)
diff --git a/java/build.xml b/java/build.xml
new file mode 100644
index 00000000..0f1b311a
--- /dev/null
+++ b/java/build.xml
@@ -0,0 +1,122 @@
+<project default="build" name="ZeroTierOneSDK" basedir=".">
+ <property environment="env"/>
+
+ <condition property="isWindows">
+ <os family="windows"/>
+ </condition>
+
+ <condition property="isMac">
+ <os family="mac"/>
+ </condition>
+
+ <target name="clean">
+ <delete dir="bin" failonerror="false"/>
+ <delete dir="classes" failonerror="false"/>
+ <delete dir="build_win32" failonerror="false"/>
+ <delete dir="build_win64" failonerror="false"/>
+ <delete dir="mac32_64" failonerror="false"/>
+ <delete dir="libs" failonerror="false"/>
+ <delete dir="obj" failonerror="false"/>
+ </target>
+
+ <target name="build_java">
+ <echo message="os.name = ${os.name}"/>
+ <echo message="os.arch = ${os.arch}"/>
+ <echo message="ant.java.version = ${ant.java.version}"/>
+ <echo message="java.version = ${java.version}"/>
+ <mkdir dir="bin"/>
+ <mkdir dir="classes"/>
+ <javac srcdir="src"
+ destdir="classes"
+ source="1.7"
+ target="1.7"
+ classpath="${env.ANDROID_PLATFORM}/android.jar"
+ includeantruntime="false"/>
+ </target>
+
+ <target name="build_android">
+ <exec dir="jni" executable="${env.NDK_BUILD_LOC}" failonerror="true">
+ <arg value="ZT1=${user.dir}/../"/>
+ </exec>
+ <copy file="libs/armeabi/libZeroTierOneJNI.so"
+ tofile="${user.dir}/classes/lib/armeabi/libZeroTierOneJNI.so"
+ overwrite="true"/>
+ <copy file="libs/arm64-v8a/libZeroTierOneJNI.so"
+ tofile="${user.dir}/classes/lib/arm64-v8a/libZeroTierOneJNI.so"
+ overwrite="true"/>
+ <copy file="libs/armeabi-v7a/libZeroTierOneJNI.so"
+ tofile="${user.dir}/classes/lib/armeabi-v7a/libZeroTierOneJNI.so"
+ overwrite="true"/>
+ <copy file="libs/x86/libZeroTierOneJNI.so"
+ tofile="${user.dir}/classes/lib/x86/libZeroTierOneJNI.so"
+ overwrite="true"/>
+ </target>
+
+
+ <target name="windows" if="isWindows">
+ <mkdir dir="build_win32"/>
+ <exec dir="build_win32/" executable="cmake" failonerror="true">
+ <arg line=".. -G&quot;Visual Studio 11 2012&quot; -DCMAKE_BUILD_TYPE=Release"/>
+ </exec>
+ <exec dir="build_win32/" executable="cmake" failonerror="true">
+ <arg line="--build . --config Release"/>
+ </exec>
+ <copy file="build_win32/Release/ZeroTierOneJNI.dll"
+ tofile="classes/lib/ZeroTierOneJNI_win32.dll"
+ overwrite="true"/>
+
+ <mkdir dir="build_win64"/>
+ <exec dir="build_win64/" executable="cmake" failonerror="true">
+ <arg line=".. -G&quot;Visual Studio 11 2012 Win64&quot; -DCMAKE_BUILD_TYPE=Release"/>
+ </exec>
+ <exec dir="build_win64/" executable="cmake" failonerror="true">
+ <arg line="--build . --config Release"/>
+ </exec>
+ <copy file="build_win64/Release/ZeroTierOneJNI.dll"
+ tofile="classes/lib/ZeroTierOneJNI_win64.dll"
+ overwrite="true"/>
+ </target>
+
+ <target name="mac" if="isMac">
+ <mkdir dir="mac32_64"/>
+ <exec dir="mac32_64/" executable="cmake" failonerror="true">
+ <arg line=".. -DCMAKE_BUILD_TYPE=Release"/>
+ </exec>
+ <exec dir="mac32_64/" executable="cmake" failonerror="true">
+ <arg line="--build . --config Release"/>
+ </exec>
+ <copy file="mac32_64/libZeroTierOneJNI.jnilib"
+ tofile="classes/lib/libZeroTierOneJNI.jnilib"
+ overwrite="true"/>
+ </target>
+
+ <target name="build" depends="build_java,build_android,windows,mac">
+ <jar destfile="bin/ZeroTierOneSDK.jar" basedir="classes"/>
+ </target>
+
+ <target name="docs">
+ <echo message="Generating Javadocs"/>
+ <mkdir dir="doc/"/>
+ <javadoc sourcepath="src/"
+ destdir="doc/"/>
+ </target>
+
+<!-- <target name="android" depends="build">
+ <echo message="OS is Android, installing..."/>
+ <copy file="libs/armeabi/libZeroTierOneJNI.so"
+ tofile="${aproj_loc}/libs/armeabi/libZeroTierOneJNI.so"
+ overwrite="true"/>
+ <copy file="libs/arm64-v8a/libZeroTierOneJNI.so"
+ tofile="${aproj_loc}/libs/arm64-v8a/libZeroTierOneJNI.so"
+ overwrite="true"/>
+ <copy file="libs/armeabi-v7a/libZeroTierOneJNI.so"
+ tofile="${aproj_loc}/libs/armeabi-v7a/libZeroTierOneJNI.so"
+ overwrite="true"/>
+ <copy file="libs/x86/libZeroTierOneJNI.so"
+ tofile="${aproj_loc}/libs/x86/libZeroTierOneJNI.so"
+ overwrite="true"/>
+ <copy file="bin/ZeroTierOneSDK.jar"
+ tofile="${aproj_loc}/libs/ZeroTierOneSDK.jar"
+ overwrite="true"/>
+ </target> -->
+</project> \ No newline at end of file
diff --git a/java/jni/Android.mk b/java/jni/Android.mk
new file mode 100644
index 00000000..bbf14348
--- /dev/null
+++ b/java/jni/Android.mk
@@ -0,0 +1,44 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := ZeroTierOneJNI
+LOCAL_C_INCLUDES := $(ZT1)/include
+LOCAL_LDLIBS := -llog
+
+# ZeroTierOne SDK source files
+LOCAL_SRC_FILES := \
+ $(ZT1)/ext/lz4/lz4.c \
+ $(ZT1)/ext/json-parser/json.c \
+ $(ZT1)/ext/http-parser/http_parser.c \
+ $(ZT1)/node/C25519.cpp \
+ $(ZT1)/node/CertificateOfMembership.cpp \
+ $(ZT1)/node/Defaults.cpp \
+ $(ZT1)/node/Dictionary.cpp \
+ $(ZT1)/node/Identity.cpp \
+ $(ZT1)/node/IncomingPacket.cpp \
+ $(ZT1)/node/InetAddress.cpp \
+ $(ZT1)/node/Multicaster.cpp \
+ $(ZT1)/node/Network.cpp \
+ $(ZT1)/node/NetworkConfig.cpp \
+ $(ZT1)/node/Node.cpp \
+ $(ZT1)/node/OutboundMulticast.cpp \
+ $(ZT1)/node/Packet.cpp \
+ $(ZT1)/node/Peer.cpp \
+ $(ZT1)/node/Poly1305.cpp \
+ $(ZT1)/node/Salsa20.cpp \
+ $(ZT1)/node/SelfAwareness.cpp \
+ $(ZT1)/node/SHA512.cpp \
+ $(ZT1)/node/Switch.cpp \
+ $(ZT1)/node/Topology.cpp \
+ $(ZT1)/node/Utils.cpp \
+ $(ZT1)/osdep/Http.cpp \
+ $(ZT1)/osdep/OSUtils.cpp
+
+# JNI Files
+LOCAL_SRC_FILES += \
+ com_zerotierone_sdk_Node.cpp \
+ ZT1_jniutils.cpp \
+ ZT1_jnicache.cpp
+
+include $(BUILD_SHARED_LIBRARY) \ No newline at end of file
diff --git a/java/jni/Application.mk b/java/jni/Application.mk
new file mode 100644
index 00000000..225169bc
--- /dev/null
+++ b/java/jni/Application.mk
@@ -0,0 +1,3 @@
+APP_ABI := armeabi armeabi-v7a arm64-v8a x86
+APP_STL := gnustl_static
+APP_CPPFLAGS += -Wall -fPIE -fstack-protector -fexceptions
diff --git a/java/jni/ZT1_jnicache.cpp b/java/jni/ZT1_jnicache.cpp
new file mode 100644
index 00000000..daa1679a
--- /dev/null
+++ b/java/jni/ZT1_jnicache.cpp
@@ -0,0 +1,242 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "ZT1_jnicache.h"
+#include "ZT1_jniutils.h"
+
+JniCache::JniCache()
+ : m_jvm(NULL)
+ , m_classes()
+ , m_fields()
+ , m_staticFields()
+ , m_methods()
+ , m_staticMethods()
+{
+ LOGD("JNI Cache Created");
+}
+
+JniCache::JniCache(JavaVM *jvm)
+ : m_jvm(jvm)
+ , m_classes()
+ , m_fields()
+ , m_staticFields()
+ , m_methods()
+ , m_staticMethods()
+{
+ LOGD("JNI Cache Created");
+}
+
+JniCache::~JniCache()
+{
+ LOGD("JNI Cache Destroyed");
+ clearCache();
+}
+
+void JniCache::clearCache()
+{
+ if(m_jvm)
+ {
+ JNIEnv *env = NULL;
+ if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
+ return;
+
+ for(ClassMap::iterator iter = m_classes.begin(), end = m_classes.end();
+ iter != end; ++iter)
+ {
+ env->DeleteGlobalRef(iter->second);
+ }
+ }
+
+ m_classes.clear();
+ m_fields.clear();
+ m_staticFields.clear();
+ m_methods.clear();
+ m_staticMethods.clear();
+}
+
+void JniCache::setJavaVM(JavaVM *jvm)
+{
+ LOGD("Assigned JVM to object");
+ m_jvm = jvm;
+}
+
+
+jclass JniCache::findClass(const std::string &name)
+{
+ if(!m_jvm)
+ return NULL;
+
+ ClassMap::iterator found = m_classes.find(name);
+
+ if(found == m_classes.end())
+ {
+ // get the class from the JVM
+ JNIEnv *env = NULL;
+ if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
+ {
+ LOGE("Error retreiving JNI Environment");
+ return NULL;
+ }
+
+ jclass localCls = env->FindClass(name.c_str());
+ if(env->ExceptionCheck())
+ {
+ LOGE("Error finding class: %s", name.c_str());
+ return NULL;
+ }
+
+ jclass cls = (jclass)env->NewGlobalRef(localCls);
+
+ m_classes.insert(std::make_pair(name, cls));
+
+ return cls;
+ }
+
+ LOGD("Returning cached %s", name.c_str());
+ return found->second;
+}
+
+
+jmethodID JniCache::findMethod(jclass cls, const std::string &methodName, const std::string &methodSig)
+{
+ if(!m_jvm)
+ return NULL;
+
+ std::string id = methodName + methodSig;
+
+ MethodMap::iterator found = m_methods.find(id);
+ if(found == m_methods.end())
+ {
+ JNIEnv *env = NULL;
+ if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
+ {
+ return NULL;
+ }
+
+ jmethodID mid = env->GetMethodID(cls, methodName.c_str(), methodSig.c_str());
+ if(env->ExceptionCheck())
+ {
+ return NULL;
+ }
+
+ m_methods.insert(std::make_pair(id, mid));
+
+ return mid;
+ }
+
+ return found->second;
+}
+
+jmethodID JniCache::findStaticMethod(jclass cls, const std::string &methodName, const std::string &methodSig)
+{
+ if(!m_jvm)
+ return NULL;
+
+ std::string id = methodName + methodSig;
+
+ MethodMap::iterator found = m_staticMethods.find(id);
+ if(found == m_staticMethods.end())
+ {
+ JNIEnv *env = NULL;
+ if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
+ {
+ return NULL;
+ }
+
+ jmethodID mid = env->GetStaticMethodID(cls, methodName.c_str(), methodSig.c_str());
+ if(env->ExceptionCheck())
+ {
+ return NULL;
+ }
+
+ m_staticMethods.insert(std::make_pair(id, mid));
+
+ return mid;
+ }
+
+ return found->second;
+}
+
+jfieldID JniCache::findField(jclass cls, const std::string &fieldName, const std::string &typeStr)
+{
+ if(!m_jvm)
+ return NULL;
+
+ std::string id = fieldName + typeStr;
+
+ FieldMap::iterator found = m_fields.find(id);
+ if(found == m_fields.end())
+ {
+ JNIEnv *env = NULL;
+ if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
+ {
+ return NULL;
+ }
+
+ jfieldID fid = env->GetFieldID(cls, fieldName.c_str(), typeStr.c_str());
+ if(env->ExceptionCheck())
+ {
+ return NULL;
+ }
+
+ m_fields.insert(std::make_pair(id, fid));
+
+ return fid;
+ }
+
+ return found->second;
+}
+
+jfieldID JniCache::findStaticField(jclass cls, const std::string &fieldName, const std::string &typeStr)
+{
+ if(!m_jvm)
+ return NULL;
+
+ std::string id = fieldName + typeStr;
+
+ FieldMap::iterator found = m_staticFields.find(id);
+ if(found == m_staticFields.end())
+ {
+ JNIEnv *env = NULL;
+ if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
+ {
+ return NULL;
+ }
+
+ jfieldID fid = env->GetStaticFieldID(cls, fieldName.c_str(), typeStr.c_str());
+ if(env->ExceptionCheck())
+ {
+ return NULL;
+ }
+
+ m_staticFields.insert(std::make_pair(id, fid));
+
+ return fid;
+ }
+
+ return found->second;
+} \ No newline at end of file
diff --git a/java/jni/ZT1_jnicache.h b/java/jni/ZT1_jnicache.h
new file mode 100644
index 00000000..43f43a08
--- /dev/null
+++ b/java/jni/ZT1_jnicache.h
@@ -0,0 +1,65 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef ZT1_JNICACHE_H_
+#define ZT1_JNICACHE_H_
+
+#include <jni.h>
+#include <map>
+#include <string>
+
+
+
+class JniCache {
+public:
+ JniCache();
+ JniCache(JavaVM *jvm);
+ ~JniCache();
+
+ void setJavaVM(JavaVM *jvm);
+ void clearCache();
+
+ jclass findClass(const std::string &name);
+ jmethodID findMethod(jclass cls, const std::string &methodName, const std::string &methodSig);
+ jmethodID findStaticMethod(jclass cls, const std::string &methodName, const std::string &methodSig);
+ jfieldID findField(jclass cls, const std::string &fieldName, const std::string &typeStr);
+ jfieldID findStaticField(jclass cls, const std::string &fieldName, const std::string &typeStr);
+private:
+ typedef std::map<std::string, jmethodID> MethodMap;
+ typedef std::map<std::string, jfieldID> FieldMap;
+ typedef std::map<std::string, jclass> ClassMap;
+
+ JavaVM *m_jvm;
+ ClassMap m_classes;
+ FieldMap m_fields;
+ FieldMap m_staticFields;
+ MethodMap m_methods;
+ MethodMap m_staticMethods;
+
+};
+
+#endif \ No newline at end of file
diff --git a/java/jni/ZT1_jniutils.cpp b/java/jni/ZT1_jniutils.cpp
new file mode 100644
index 00000000..5bda6ae9
--- /dev/null
+++ b/java/jni/ZT1_jniutils.cpp
@@ -0,0 +1,875 @@
+#include "ZT1_jniutils.h"
+#include "ZT1_jnicache.h"
+#include <string>
+#include <assert.h>
+
+extern JniCache cache;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+jobject createResultObject(JNIEnv *env, ZT1_ResultCode code)
+{
+ jclass resultClass = NULL;
+
+ jobject resultObject = NULL;
+
+ resultClass = cache.findClass("com/zerotier/sdk/ResultCode");
+ if(resultClass == NULL)
+ {
+ return NULL; // exception thrown
+ }
+
+ std::string fieldName;
+ switch(code)
+ {
+ case ZT1_RESULT_OK:
+ fieldName = "RESULT_OK";
+ break;
+ case ZT1_RESULT_FATAL_ERROR_OUT_OF_MEMORY:
+ fieldName = "RESULT_FATAL_ERROR_OUT_OF_MEMORY";
+ break;
+ case ZT1_RESULT_FATAL_ERROR_DATA_STORE_FAILED:
+ fieldName = "RESULT_FATAL_ERROR_DATA_STORE_FAILED";
+ break;
+ case ZT1_RESULT_ERROR_NETWORK_NOT_FOUND:
+ fieldName = "RESULT_ERROR_NETWORK_NOT_FOUND";
+ break;
+ case ZT1_RESULT_FATAL_ERROR_INTERNAL:
+ default:
+ fieldName = "RESULT_FATAL_ERROR_INTERNAL";
+ break;
+ }
+
+ jfieldID enumField = cache.findStaticField(resultClass, fieldName.c_str(), "Lcom/zerotier/sdk/ResultCode;");
+
+ resultObject = env->GetStaticObjectField(resultClass, enumField);
+
+ return resultObject;
+}
+
+
+jobject createVirtualNetworkStatus(JNIEnv *env, ZT1_VirtualNetworkStatus status)
+{
+ jobject statusObject = NULL;
+
+ jclass statusClass = cache.findClass("com/zerotier/sdk/VirtualNetworkStatus");
+ if(statusClass == NULL)
+ {
+ return NULL; // exception thrown
+ }
+
+ std::string fieldName;
+ switch(status)
+ {
+ case ZT1_NETWORK_STATUS_REQUESTING_CONFIGURATION:
+ fieldName = "NETWORK_STATUS_REQUESTING_CONFIGURATION";
+ break;
+ case ZT1_NETWORK_STATUS_OK:
+ fieldName = "NETWORK_STATUS_OK";
+ break;
+ case ZT1_NETWORK_STATUS_ACCESS_DENIED:
+ fieldName = "NETWORK_STATUS_ACCESS_DENIED";
+ break;
+ case ZT1_NETWORK_STATUS_NOT_FOUND:
+ fieldName = "NETWORK_STATUS_NOT_FOUND";
+ break;
+ case ZT1_NETWORK_STATUS_PORT_ERROR:
+ fieldName = "NETWORK_STATUS_PORT_ERROR";
+ break;
+ case ZT1_NETWORK_STATUS_CLIENT_TOO_OLD:
+ fieldName = "NETWORK_STATUS_CLIENT_TOO_OLD";
+ break;
+ }
+
+ jfieldID enumField = cache.findStaticField(statusClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkStatus;");
+
+ statusObject = env->GetStaticObjectField(statusClass, enumField);
+
+ return statusObject;
+}
+
+jobject createEvent(JNIEnv *env, ZT1_Event event)
+{
+ jclass eventClass = NULL;
+ jobject eventObject = NULL;
+
+ eventClass = cache.findClass("com/zerotier/sdk/Event");
+ if(eventClass == NULL)
+ {
+ return NULL;
+ }
+
+ std::string fieldName;
+ switch(event)
+ {
+ case ZT1_EVENT_UP:
+ fieldName = "EVENT_UP";
+ break;
+ case ZT1_EVENT_OFFLINE:
+ fieldName = "EVENT_OFFLINE";
+ break;
+ case ZT1_EVENT_ONLINE:
+ fieldName = "EVENT_ONLINE";
+ break;
+ case ZT1_EVENT_DOWN:
+ fieldName = "EVENT_DOWN";
+ break;
+ case ZT1_EVENT_FATAL_ERROR_IDENTITY_COLLISION:
+ fieldName = "EVENT_FATAL_ERROR_IDENTITY_COLLISION";
+ break;
+ case ZT1_EVENT_SAW_MORE_RECENT_VERSION:
+ fieldName = "EVENT_SAW_MORE_RECENT_VERSION";
+ break;
+ case ZT1_EVENT_AUTHENTICATION_FAILURE:
+ fieldName = "EVENT_AUTHENTICATION_FAILURE";
+ break;
+ case ZT1_EVENT_INVALID_PACKET:
+ fieldName = "EVENT_INVALID_PACKET";
+ break;
+ case ZT1_EVENT_TRACE:
+ fieldName = "EVENT_TRACE";
+ break;
+ }
+
+ jfieldID enumField = cache.findStaticField(eventClass, fieldName.c_str(), "Lcom/zerotier/sdk/Event;");
+
+ eventObject = env->GetStaticObjectField(eventClass, enumField);
+
+ return eventObject;
+}
+
+jobject createPeerRole(JNIEnv *env, ZT1_PeerRole role)
+{
+ jclass peerRoleClass = NULL;
+ jobject peerRoleObject = NULL;
+
+ peerRoleClass = cache.findClass("com/zerotier/sdk/PeerRole");
+ if(peerRoleClass == NULL)
+ {
+ return NULL;
+ }
+
+ std::string fieldName;
+ switch(role)
+ {
+ case ZT1_PEER_ROLE_LEAF:
+ fieldName = "PEER_ROLE_LEAF";
+ break;
+ case ZT1_PEER_ROLE_HUB:
+ fieldName = "PEER_ROLE_HUB";
+ break;
+ case ZT1_PEER_ROLE_SUPERNODE:
+ fieldName = "PEER_ROLE_SUPERNODE";
+ break;
+ }
+
+ jfieldID enumField = cache.findStaticField(peerRoleClass, fieldName.c_str(), "Lcom/zerotier/sdk/PeerRole;");
+
+ peerRoleObject = env->GetStaticObjectField(peerRoleClass, enumField);
+
+ return peerRoleObject;
+}
+
+jobject createVirtualNetworkType(JNIEnv *env, ZT1_VirtualNetworkType type)
+{
+ jclass vntypeClass = NULL;
+ jobject vntypeObject = NULL;
+
+ vntypeClass = cache.findClass("com/zerotier/sdk/VirtualNetworkType");
+ if(vntypeClass == NULL)
+ {
+ return NULL;
+ }
+
+ std::string fieldName;
+ switch(type)
+ {
+ case ZT1_NETWORK_TYPE_PRIVATE:
+ fieldName = "NETWORK_TYPE_PRIVATE";
+ break;
+ case ZT1_NETWORK_TYPE_PUBLIC:
+ fieldName = "NETWORK_TYPE_PUBLIC";
+ break;
+ }
+
+ jfieldID enumField = cache.findStaticField(vntypeClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkType;");
+ vntypeObject = env->GetStaticObjectField(vntypeClass, enumField);
+ return vntypeObject;
+}
+
+jobject createVirtualNetworkConfigOperation(JNIEnv *env, ZT1_VirtualNetworkConfigOperation op)
+{
+ jclass vnetConfigOpClass = NULL;
+ jobject vnetConfigOpObject = NULL;
+
+ vnetConfigOpClass = cache.findClass("com/zerotier/sdk/VirtualNetworkConfigOperation");
+ if(vnetConfigOpClass == NULL)
+ {
+ return NULL;
+ }
+
+ std::string fieldName;
+ switch(op)
+ {
+ case ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_UP:
+ fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_UP";
+ break;
+ case ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE:
+ fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE";
+ break;
+ case ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN:
+ fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN";
+ break;
+ case ZT1_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY:
+ fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY";
+ break;
+ }
+
+ jfieldID enumField = cache.findStaticField(vnetConfigOpClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkConfigOperation;");
+ vnetConfigOpObject = env->GetStaticObjectField(vnetConfigOpClass, enumField);
+ return vnetConfigOpObject;
+}
+
+jobject newArrayList(JNIEnv *env)
+{
+ jclass arrayListClass = NULL;
+ jmethodID arrayList_constructor = NULL;
+
+ arrayListClass = cache.findClass("java/util/ArrayList");
+ if(arrayListClass == NULL)
+ {
+ return NULL;
+ }
+
+ arrayList_constructor = cache.findMethod(
+ arrayListClass, "<init>", "()V");
+ if(arrayList_constructor == NULL)
+ {
+ return NULL;
+ }
+
+ jobject arrayListObj = env->NewObject(arrayListClass, arrayList_constructor);
+
+ return arrayListObj;
+}
+
+bool appendItemToArrayList(JNIEnv *env, jobject array, jobject object)
+{
+ assert(array != NULL);
+ assert(object != NULL);
+
+ jclass arrayListClass = NULL;
+ jmethodID arrayList_add = NULL;
+
+ arrayListClass = cache.findClass("java/util/ArrayList");
+ if(arrayListClass == NULL)
+ {
+ return NULL;
+ }
+
+ arrayList_add = cache.findMethod(arrayListClass, "add", "(Ljava.lang.Object;)Z");
+ if(arrayList_add == NULL)
+ {
+ return false;
+ }
+
+ return env->CallBooleanMethod(array, arrayList_add, object);
+}
+
+jobject newInetAddress(JNIEnv *env, const sockaddr_storage &addr)
+{
+ jclass inetAddressClass = NULL;
+ jmethodID inetAddress_getByAddress = NULL;
+
+ inetAddressClass = cache.findClass("java/net/InetAddress");
+ if(inetAddressClass == NULL)
+ {
+ return NULL;
+ }
+
+ inetAddress_getByAddress = cache.findStaticMethod(
+ inetAddressClass, "getByAddress", "([B)Ljava/net/InetAddress;");
+ if(inetAddress_getByAddress == NULL)
+ {
+ return NULL;
+ }
+
+ jobject inetAddressObj = NULL;
+ switch(addr.ss_family)
+ {
+ case AF_INET6:
+ {
+ sockaddr_in6 *ipv6 = (sockaddr_in6*)&addr;
+ jbyteArray buff = env->NewByteArray(16);
+ if(buff == NULL)
+ {
+ return NULL;
+ }
+
+ env->SetByteArrayRegion(buff, 0, 16, (jbyte*)ipv6->sin6_addr.s6_addr);
+ inetAddressObj = env->CallStaticObjectMethod(
+ inetAddressClass, inetAddress_getByAddress, buff);
+ }
+ break;
+ case AF_INET:
+ {
+ sockaddr_in *ipv4 = (sockaddr_in*)&addr;
+ jbyteArray buff = env->NewByteArray(4);
+ if(buff == NULL)
+ {
+ return NULL;
+ }
+
+ env->SetByteArrayRegion(buff, 0, 4, (jbyte*)&ipv4->sin_addr);
+ inetAddressObj = env->CallStaticObjectMethod(
+ inetAddressClass, inetAddress_getByAddress, buff);
+ }
+ break;
+ }
+
+ return inetAddressObj;
+}
+
+jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr)
+{
+ jclass inetSocketAddressClass = NULL;
+ jmethodID inetSocketAddress_constructor = NULL;
+
+ inetSocketAddressClass = cache.findClass("java/net/InetSocketAddress");
+ if(inetSocketAddressClass == NULL)
+ {
+ return NULL;
+ }
+
+ jobject inetAddressObject = newInetAddress(env, addr);
+
+ if(inetAddressObject == NULL)
+ {
+ return NULL;
+ }
+
+ inetSocketAddress_constructor = cache.findMethod(
+ inetSocketAddressClass, "<init>", "(Ljava/net/InetAddress;I)V");
+ if(inetSocketAddress_constructor == NULL)
+ {
+ return NULL;
+ }
+
+ int port = 0;
+ switch(addr.ss_family)
+ {
+ case AF_INET6:
+ {
+ sockaddr_in6 *ipv6 = (sockaddr_in6*)&addr;
+ port = ntohs(ipv6->sin6_port);
+ }
+ break;
+ case AF_INET:
+ {
+ sockaddr_in *ipv4 = (sockaddr_in*)&addr;
+ port = ntohs(ipv4->sin_port);
+ }
+ break;
+ };
+
+ jobject inetSocketAddressObject = env->NewObject(inetSocketAddressClass, inetSocketAddress_constructor, inetAddressObject, port);
+ return inetSocketAddressObject;
+}
+
+jobject newMulticastGroup(JNIEnv *env, const ZT1_MulticastGroup &mc)
+{
+ jclass multicastGroupClass = NULL;
+ jmethodID multicastGroup_constructor = NULL;
+
+ jfieldID macField = NULL;
+ jfieldID adiField = NULL;
+
+ multicastGroupClass = cache.findClass("com/zerotier/sdk/MulticastGroup");
+ if(multicastGroupClass == NULL)
+ {
+ return NULL;
+ }
+
+ multicastGroup_constructor = cache.findMethod(
+ multicastGroupClass, "<init>", "()V");
+ if(multicastGroup_constructor == NULL)
+ {
+ return NULL;
+ }
+
+ jobject multicastGroupObj = env->NewObject(multicastGroupClass, multicastGroup_constructor);
+ if(multicastGroupObj == NULL)
+ {
+ return NULL;
+ }
+
+ if(macField == NULL)
+ {
+ macField = cache.findField(multicastGroupClass, "mac", "J");
+ if(macField == NULL)
+ {
+ return NULL;
+ }
+ }
+
+ if(adiField == NULL)
+ {
+ adiField = cache.findField(multicastGroupClass, "adi", "J");
+ if(adiField == NULL)
+ {
+ return NULL;
+ }
+ }
+
+ env->SetLongField(multicastGroupObj, macField, mc.mac);
+ env->SetLongField(multicastGroupObj, adiField, mc.adi);
+
+ return multicastGroupObj;
+}
+
+jobject newPeerPhysicalPath(JNIEnv *env, const ZT1_PeerPhysicalPath &ppp)
+{
+ jclass pppClass = NULL;
+
+ jfieldID addressField = NULL;
+ jfieldID lastSendField = NULL;
+ jfieldID lastReceiveField = NULL;
+ jfieldID fixedField = NULL;
+ jfieldID activeField = NULL;
+ jfieldID preferredField = NULL;
+
+ jmethodID ppp_constructor = NULL;
+
+ pppClass = cache.findClass("com/zerotier/sdk/PeerPhysicalPath");
+ if(pppClass == NULL)
+ {
+ return NULL;
+ }
+
+ addressField = cache.findField(pppClass, "address", "Ljava/net/InetAddress;");
+ if(addressField == NULL)
+ {
+ return NULL;
+ }
+
+ lastSendField = cache.findField(pppClass, "lastSend", "J");
+ if(lastSendField == NULL)
+ {
+ return NULL;
+ }
+
+ lastReceiveField = cache.findField(pppClass, "lastReceive", "J");
+ if(lastReceiveField == NULL)
+ {
+ return NULL;
+ }
+
+ fixedField = cache.findField(pppClass, "fixed", "Z");
+ if(fixedField == NULL)
+ {
+ return NULL;
+ }
+
+ activeField = cache.findField(pppClass, "active", "Z");
+ if(activeField == NULL)
+ {
+ return NULL;
+ }
+
+ preferredField = cache.findField(pppClass, "preferred", "Z");
+ if(preferredField == NULL)
+ {
+ return NULL;
+ }
+
+ ppp_constructor = cache.findMethod(pppClass, "<init>", "()V");
+ if(ppp_constructor == NULL)
+ {
+ return NULL;
+ }
+
+ jobject pppObject = env->NewObject(pppClass, ppp_constructor);
+ if(pppObject == NULL)
+ {
+ return NULL; // out of memory
+ }
+
+ jobject addressObject = newInetAddress(env, ppp.address);
+
+ env->SetObjectField(pppClass, addressField, addressObject);
+ env->SetLongField(pppClass, lastSendField, ppp.lastSend);
+ env->SetLongField(pppClass, lastReceiveField, ppp.lastReceive);
+ env->SetBooleanField(pppClass, fixedField, ppp.fixed);
+ env->SetBooleanField(pppClass, activeField, ppp.active);
+ env->SetBooleanField(pppClass, preferredField, ppp.preferred);
+
+ return pppObject;
+}
+
+jobject newPeer(JNIEnv *env, const ZT1_Peer &peer)
+{
+ jclass peerClass = NULL;
+
+ jfieldID addressField = NULL;
+ jfieldID lastUnicastFrameField = NULL;
+ jfieldID lastMulticastFrameField = NULL;
+ jfieldID versionMajorField = NULL;
+ jfieldID versionMinorField = NULL;
+ jfieldID versionRevField = NULL;
+ jfieldID latencyField = NULL;
+ jfieldID roleField = NULL;
+ jfieldID pathsField = NULL;
+
+ jmethodID peer_constructor = NULL;
+
+ peerClass = cache.findClass("com/zerotier/sdk/Peer");
+ if(peerClass == NULL)
+ {
+ return NULL;
+ }
+
+ addressField = cache.findField(peerClass, "address", "J");
+ if(addressField == NULL)
+ {
+ return NULL;
+ }
+
+ lastUnicastFrameField = cache.findField(peerClass, "lastUnicastFrame", "J");
+ if(lastUnicastFrameField == NULL)
+ {
+ return NULL;
+ }
+
+ lastMulticastFrameField = cache.findField(peerClass, "lastMulticastFrame", "J");
+ if(lastMulticastFrameField == NULL)
+ {
+ return NULL;
+ }
+
+ versionMajorField = cache.findField(peerClass, "versionMajor", "I");
+ if(versionMajorField == NULL)
+ {
+ return NULL;
+ }
+
+ versionMinorField = cache.findField(peerClass, "versionMinor", "I");
+ if(versionMinorField == NULL)
+ {
+ return NULL;
+ }
+
+ versionRevField = cache.findField(peerClass, "versionRev", "I");
+ if(versionRevField == NULL)
+ {
+ return NULL;
+ }
+
+ latencyField = cache.findField(peerClass, "latency", "I");
+ if(latencyField == NULL)
+ {
+ return NULL;
+ }
+
+ roleField = cache.findField(peerClass, "role", "Lcom/zerotier/sdk/PeerRole;");
+ if(roleField == NULL)
+ {
+ return NULL;
+ }
+
+ pathsField = cache.findField(peerClass, "paths", "Ljava.util.ArrayList;");
+ if(pathsField == NULL)
+ {
+ return NULL;
+ }
+
+ peer_constructor = cache.findMethod(peerClass, "<init>", "()V");
+ if(peer_constructor == NULL)
+ {
+ return NULL;
+ }
+
+ jobject peerObject = env->NewObject(peerClass, peer_constructor);
+ if(peerObject == NULL)
+ {
+ return NULL; // out of memory
+ }
+
+ env->SetLongField(peerClass, addressField, (jlong)peer.address);
+ env->SetLongField(peerClass, lastUnicastFrameField, (jlong)peer.lastUnicastFrame);
+ env->SetLongField(peerClass, lastMulticastFrameField, (jlong)peer.lastMulticastFrame);
+ env->SetIntField(peerClass, versionMajorField, peer.versionMajor);
+ env->SetIntField(peerClass, versionMinorField, peer.versionMinor);
+ env->SetIntField(peerClass, versionRevField, peer.versionRev);
+ env->SetIntField(peerClass, latencyField, peer.latency);
+ env->SetObjectField(peerClass, roleField, createPeerRole(env, peer.role));
+
+ jobject arrayObject = newArrayList(env);
+ for(unsigned int i = 0; i < peer.pathCount; ++i)
+ {
+ jobject path = newPeerPhysicalPath(env, peer.paths[i]);
+ appendItemToArrayList(env, arrayObject, path);
+ }
+
+ env->SetObjectField(peerClass, pathsField, arrayObject);
+
+ return peerObject;
+}
+
+jobject newNetworkConfig(JNIEnv *env, const ZT1_VirtualNetworkConfig &vnetConfig)
+{
+ jclass vnetConfigClass = NULL;
+ jmethodID vnetConfig_constructor = NULL;
+ jfieldID nwidField = NULL;
+ jfieldID macField = NULL;
+ jfieldID nameField = NULL;
+ jfieldID statusField = NULL;
+ jfieldID typeField = NULL;
+ jfieldID mtuField = NULL;
+ jfieldID dhcpField = NULL;
+ jfieldID bridgeField = NULL;
+ jfieldID broadcastEnabledField = NULL;
+ jfieldID portErrorField = NULL;
+ jfieldID enabledField = NULL;
+ jfieldID netconfRevisionField = NULL;
+ jfieldID multicastSubscriptionsField = NULL;
+ jfieldID assignedAddressesField = NULL;
+
+ vnetConfigClass = cache.findClass("com/zerotier/sdk/VirtualNetworkConfig");
+ if(vnetConfigClass == NULL)
+ {
+ LOGE("Couldn't find com.zerotier.sdk.VirtualNetworkConfig");
+ return NULL;
+ }
+
+ vnetConfig_constructor = cache.findMethod(
+ vnetConfigClass, "<init>", "()V");
+ if(vnetConfig_constructor == NULL)
+ {
+ LOGE("Couldn't find VirtualNetworkConfig Constructor");
+ return NULL;
+ }
+
+ jobject vnetConfigObj = env->NewObject(vnetConfigClass, vnetConfig_constructor);
+ if(vnetConfigObj == NULL)
+ {
+ LOGE("Error creating new VirtualNetworkConfig object");
+ return NULL;
+ }
+
+ nwidField = cache.findField(vnetConfigClass, "nwid", "J");
+ if(nwidField == NULL)
+ {
+ LOGE("Error getting nwid field");
+ return NULL;
+ }
+
+ macField = cache.findField(vnetConfigClass, "mac", "J");
+ if(macField == NULL)
+ {
+ LOGE("Error getting mac field");
+ return NULL;
+ }
+
+ nameField = cache.findField(vnetConfigClass, "name", "Ljava/lang/String;");
+ if(nameField == NULL)
+ {
+ LOGE("Error getting name field");
+ return NULL;
+ }
+
+ statusField = cache.findField(vnetConfigClass, "status", "Lcom/zerotier/sdk/VirtualNetworkStatus;");
+ if(statusField == NULL)
+ {
+ LOGE("Error getting status field");
+ return NULL;
+ }
+
+ typeField = cache.findField(vnetConfigClass, "type", "Lcom/zerotier/sdk/VirtualNetworkType;");
+ if(typeField == NULL)
+ {
+ LOGE("Error getting type field");
+ return NULL;
+ }
+
+ mtuField = cache.findField(vnetConfigClass, "mtu", "I");
+ if(mtuField == NULL)
+ {
+ LOGE("Error getting mtu field");
+ return NULL;
+ }
+
+ dhcpField = cache.findField(vnetConfigClass, "dhcp", "Z");
+ if(dhcpField == NULL)
+ {
+ LOGE("Error getting dhcp field");
+ return NULL;
+ }
+
+ bridgeField = cache.findField(vnetConfigClass, "bridge", "Z");
+ if(bridgeField == NULL)
+ {
+ LOGE("Error getting bridge field");
+ return NULL;
+ }
+
+ broadcastEnabledField = cache.findField(vnetConfigClass, "broadcastEnabled", "Z");
+ if(broadcastEnabledField == NULL)
+ {
+ LOGE("Error getting broadcastEnabled field");
+ return NULL;
+ }
+
+ portErrorField = cache.findField(vnetConfigClass, "portError", "I");
+ if(portErrorField == NULL)
+ {
+ LOGE("Error getting portError field");
+ return NULL;
+ }
+
+ enabledField = cache.findField(vnetConfigClass, "enabled", "Z");
+ if(enabledField == NULL)
+ {
+ LOGE("Error getting enabled field");
+ return NULL;
+ }
+
+ netconfRevisionField = cache.findField(vnetConfigClass, "netconfRevision", "J");
+ if(netconfRevisionField == NULL)
+ {
+ LOGE("Error getting netconfRevision field");
+ return NULL;
+ }
+
+ multicastSubscriptionsField = cache.findField(vnetConfigClass, "multicastSubscriptions", "Ljava/util/ArrayList;");
+ if(multicastSubscriptionsField == NULL)
+ {
+ LOGE("Error getting multicastSubscriptions field");
+ return NULL;
+ }
+
+ assignedAddressesField = cache.findField(vnetConfigClass, "assignedAddresses", "Ljava/util/ArrayList;");
+ if(assignedAddressesField == NULL)
+ {
+ LOGE("Error getting assignedAddresses field");
+ return NULL;
+ }
+
+ env->SetLongField(vnetConfigObj, nwidField, vnetConfig.nwid);
+ env->SetLongField(vnetConfigObj, macField, vnetConfig.mac);
+ jstring nameStr = env->NewStringUTF(vnetConfig.name);
+ if(nameStr == NULL)
+ {
+ return NULL; // out of memory
+ }
+ env->SetObjectField(vnetConfigObj, nameField, nameStr);
+
+ jobject statusObject = createVirtualNetworkStatus(env, vnetConfig.status);
+ if(statusObject == NULL)
+ {
+ return NULL;
+ }
+ env->SetObjectField(vnetConfigObj, statusField, statusObject);
+
+ jobject typeObject = createVirtualNetworkType(env, vnetConfig.type);
+ if(typeObject == NULL)
+ {
+ return NULL;
+ }
+ env->SetObjectField(vnetConfigObj, typeField, typeObject);
+
+ env->SetIntField(vnetConfigObj, mtuField, vnetConfig.mtu);
+ env->SetBooleanField(vnetConfigObj, dhcpField, vnetConfig.dhcp);
+ env->SetBooleanField(vnetConfigObj, bridgeField, vnetConfig.bridge);
+ env->SetBooleanField(vnetConfigObj, broadcastEnabledField, vnetConfig.broadcastEnabled);
+ env->SetIntField(vnetConfigObj, portErrorField, vnetConfig.portError);
+
+
+ jobject mcastSubsArrayObj = newArrayList(env);
+ for(unsigned int i = 0; i < vnetConfig.multicastSubscriptionCount; ++i)
+ {
+ jobject mcastObj = newMulticastGroup(env, vnetConfig.multicastSubscriptions[i]);
+ appendItemToArrayList(env, mcastSubsArrayObj, mcastObj);
+ }
+ env->SetObjectField(vnetConfigObj, multicastSubscriptionsField, mcastSubsArrayObj);
+
+
+ jobject assignedAddrArrayObj = newArrayList(env);
+ for(unsigned int i = 0; i < vnetConfig.assignedAddressCount; ++i)
+ {
+ jobject inetAddrObj = newInetAddress(env, vnetConfig.assignedAddresses[i]);
+ appendItemToArrayList(env, assignedAddrArrayObj, inetAddrObj);
+ }
+
+ env->SetObjectField(vnetConfigObj, assignedAddressesField, assignedAddrArrayObj);
+
+ return vnetConfigObj;
+}
+
+jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags)
+{
+ // create a com.zerotier.sdk.Version object
+ jclass versionClass = NULL;
+ jmethodID versionConstructor = NULL;
+
+ versionClass = cache.findClass("com/zerotier/sdk/Version");
+ if(versionClass == NULL)
+ {
+ return NULL;
+ }
+
+ versionConstructor = cache.findMethod(
+ versionClass, "<init>", "()V");
+ if(versionConstructor == NULL)
+ {
+ return NULL;
+ }
+
+ jobject versionObj = env->NewObject(versionClass, versionConstructor);
+ if(versionObj == NULL)
+ {
+ return NULL;
+ }
+
+ // copy data to Version object
+ jfieldID majorField = NULL;
+ jfieldID minorField = NULL;
+ jfieldID revisionField = NULL;
+ jfieldID featureFlagsField = NULL;
+
+ majorField = cache.findField(versionClass, "major", "I");
+ if(majorField == NULL)
+ {
+ return NULL;
+ }
+
+ minorField = cache.findField(versionClass, "minor", "I");
+ if(minorField == NULL)
+ {
+ return NULL;
+ }
+
+ revisionField = cache.findField(versionClass, "revision", "I");
+ if(revisionField == NULL)
+ {
+ return NULL;
+ }
+
+ featureFlagsField = cache.findField(versionClass, "featureFlags", "J");
+ if(featureFlagsField == NULL)
+ {
+ return NULL;
+ }
+
+ env->SetIntField(versionObj, majorField, (jint)major);
+ env->SetIntField(versionObj, minorField, (jint)minor);
+ env->SetIntField(versionObj, revisionField, (jint)rev);
+ env->SetLongField(versionObj, featureFlagsField, (jlong)featureFlags);
+
+ return versionObj;
+}
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/java/jni/ZT1_jniutils.h b/java/jni/ZT1_jniutils.h
new file mode 100644
index 00000000..d8baf85d
--- /dev/null
+++ b/java/jni/ZT1_jniutils.h
@@ -0,0 +1,50 @@
+#ifndef ZT1_jniutils_h_
+#define ZT1_jniutils_h_
+#include <stdio.h>
+#include <jni.h>
+#include <ZeroTierOne.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LOG_TAG "ZeroTierOneJNI"
+
+#if __ANDROID__
+#include <android/log.h>
+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
+#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#else
+#define LOGI(...) fprintf(stdout, __VA_ARGS__)
+#define LOGD(...) fprintf(stdout, __VA_ARGS__)
+#define LOGE(...) fprintf(stdout, __VA_ARGS__)
+#endif
+
+jobject createResultObject(JNIEnv *env, ZT1_ResultCode code);
+jobject createVirtualNetworkStatus(JNIEnv *env, ZT1_VirtualNetworkStatus status);
+jobject createVirtualNetworkType(JNIEnv *env, ZT1_VirtualNetworkType type);
+jobject createEvent(JNIEnv *env, ZT1_Event event);
+jobject createPeerRole(JNIEnv *env, ZT1_PeerRole role);
+jobject createVirtualNetworkConfigOperation(JNIEnv *env, ZT1_VirtualNetworkConfigOperation op);
+
+jobject newArrayList(JNIEnv *env);
+bool appendItemToArrayList(JNIEnv *env, jobject array, jobject object);
+
+jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr);
+jobject newInetAddress(JNIEnv *env, const sockaddr_storage &addr);
+
+jobject newMulticastGroup(JNIEnv *env, const ZT1_MulticastGroup &mc);
+
+jobject newPeer(JNIEnv *env, const ZT1_Peer &peer);
+jobject newPeerPhysicalPath(JNIEnv *env, const ZT1_PeerPhysicalPath &ppp);
+
+jobject newNetworkConfig(JNIEnv *env, const ZT1_VirtualNetworkConfig &config);
+
+jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp
new file mode 100644
index 00000000..65ec364b
--- /dev/null
+++ b/java/jni/com_zerotierone_sdk_Node.cpp
@@ -0,0 +1,1197 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "com_zerotierone_sdk_Node.h"
+#include "ZT1_jniutils.h"
+#include "ZT1_jnicache.h"
+
+#include <ZeroTierOne.h>
+
+#include <map>
+#include <string>
+#include <assert.h>
+#include <string.h>
+
+// global static JNI Cache Object
+JniCache cache;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+namespace {
+ struct JniRef
+ {
+ JniRef()
+ : jvm(NULL)
+ , node(NULL)
+ , dataStoreGetListener(NULL)
+ , dataStorePutListener(NULL)
+ , packetSender(NULL)
+ , eventListener(NULL)
+ , frameListener(NULL)
+ , configListener(NULL)
+ {}
+
+ ~JniRef()
+ {
+ JNIEnv *env = NULL;
+ jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+ env->DeleteGlobalRef(dataStoreGetListener);
+ env->DeleteGlobalRef(dataStorePutListener);
+ env->DeleteGlobalRef(packetSender);
+ env->DeleteGlobalRef(eventListener);
+ env->DeleteGlobalRef(frameListener);
+ env->DeleteGlobalRef(configListener);
+ }
+
+ uint64_t id;
+
+ JavaVM *jvm;
+
+ ZT1_Node *node;
+
+ jobject dataStoreGetListener;
+ jobject dataStorePutListener;
+ jobject packetSender;
+ jobject eventListener;
+ jobject frameListener;
+ jobject configListener;
+ };
+
+
+ int VirtualNetworkConfigFunctionCallback(
+ ZT1_Node *node,
+ void *userData,
+ uint64_t nwid,
+ enum ZT1_VirtualNetworkConfigOperation operation,
+ const ZT1_VirtualNetworkConfig *config)
+ {
+ JniRef *ref = (JniRef*)userData;
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+ jclass configListenerClass = env->GetObjectClass(ref->configListener);
+ if(configListenerClass == NULL)
+ {
+ LOGE("Couldn't find class for VirtualNetworkConfigListener instance");
+ return -1;
+ }
+
+ jmethodID configListenerCallbackMethod = cache.findMethod(configListenerClass,
+ "onNetworkConfigurationUpdated",
+ "(JLcom/zerotier/sdk/VirtualNetworkConfigOperation;Lcom/zerotier/sdk/VirtualNetworkConfig;)I");
+ if(configListenerCallbackMethod == NULL)
+ {
+ LOGE("Couldn't find onVirtualNetworkFrame() method");
+ return -2;
+ }
+
+ jobject operationObject = createVirtualNetworkConfigOperation(env, operation);
+ if(operationObject == NULL)
+ {
+ LOGE("Error creating VirtualNetworkConfigOperation object");
+ return -3;
+ }
+
+ jobject networkConfigObject = newNetworkConfig(env, *config);
+ if(networkConfigObject == NULL)
+ {
+ LOGE("Error creating VirtualNetworkConfig object");
+ return -4;
+ }
+
+ return env->CallIntMethod(
+ ref->configListener,
+ configListenerCallbackMethod,
+ (jlong)nwid, operationObject, networkConfigObject);
+ }
+
+ void VirtualNetworkFrameFunctionCallback(ZT1_Node *node,void *userData,
+ uint64_t nwid,
+ uint64_t sourceMac,
+ uint64_t destMac,
+ unsigned int etherType,
+ unsigned int vlanid,
+ const void *frameData,
+ unsigned int frameLength)
+ {
+ JniRef *ref = (JniRef*)userData;
+ assert(ref->node == node);
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+
+ jclass frameListenerClass = env->GetObjectClass(ref->frameListener);
+ if(frameListenerClass == NULL)
+ {
+ LOGE("Couldn't find class for VirtualNetworkFrameListener instance");
+ return;
+ }
+
+ jmethodID frameListenerCallbackMethod = cache.findMethod(
+ frameListenerClass,
+ "onVirtualNetworkFrame", "(JJJJJ[B)V");
+ if(frameListenerCallbackMethod == NULL)
+ {
+ LOGE("Couldn't find onVirtualNetworkFrame() method");
+ return;
+ }
+
+ jbyteArray dataArray = env->NewByteArray(frameLength);
+ env->SetByteArrayRegion(dataArray, 0, frameLength, (jbyte*)frameData);
+
+ env->CallVoidMethod(ref->frameListener, frameListenerCallbackMethod, nwid, sourceMac, destMac, etherType, vlanid, dataArray);
+ }
+
+
+ void EventCallback(ZT1_Node *node,void *userData,enum ZT1_Event event, const void *data)
+ {
+ JniRef *ref = (JniRef*)userData;
+ assert(ref->node == node);
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+
+ jclass eventListenerClass = env->GetObjectClass(ref->eventListener);
+ if(eventListenerClass == NULL)
+ {
+ LOGE("Couldn't class for EventListener instance");
+ return;
+ }
+
+ jmethodID onEventMethod = cache.findMethod(eventListenerClass,
+ "onEvent", "(Lcom/zerotier/sdk/Event;)V");
+ if(onEventMethod == NULL)
+ {
+ LOGE("Couldn't find onEvent method");
+ return;
+ }
+
+
+ jmethodID onOutOfDateMethod = cache.findMethod(eventListenerClass,
+ "onOutOfDate", "(Lcom/zerotier/sdk/Version;)V");
+ if(onOutOfDateMethod == NULL)
+ {
+ LOGE("Couldn't find onOutOfDate method");
+ return;
+ }
+
+
+ jmethodID onNetworkErrorMethod = cache.findMethod(eventListenerClass,
+ "onNetworkError", "(Lcom/zerotier/sdk/Event;Ljava/net/InetSocketAddress;)V");
+ if(onNetworkErrorMethod == NULL)
+ {
+ LOGE("Couldn't find onNetworkError method");
+ return;
+ }
+
+
+ jmethodID onTraceMethod = cache.findMethod(eventListenerClass,
+ "onTrace", "(Ljava/lang/String;)V");
+ if(onTraceMethod == NULL)
+ {
+ LOGE("Couldn't find onTrace method");
+ return;
+ }
+
+ jobject eventObject = createEvent(env, event);
+ if(eventObject == NULL)
+ {
+ return;
+ }
+
+ switch(event)
+ {
+ case ZT1_EVENT_UP:
+ case ZT1_EVENT_OFFLINE:
+ case ZT1_EVENT_ONLINE:
+ case ZT1_EVENT_DOWN:
+ case ZT1_EVENT_FATAL_ERROR_IDENTITY_COLLISION:
+ {
+ // call onEvent()
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ }
+ break;
+ case ZT1_EVENT_SAW_MORE_RECENT_VERSION:
+ {
+ // call onOutOfDate()
+ if(data != NULL)
+ {
+ int *version = (int*)data;
+ jobject verisonObj = newVersion(env, version[0], version[1], version[2], 0);
+ env->CallVoidMethod(ref->eventListener, onOutOfDateMethod, verisonObj);
+ }
+ }
+ break;
+ case ZT1_EVENT_AUTHENTICATION_FAILURE:
+ case ZT1_EVENT_INVALID_PACKET:
+ {
+ // call onNetworkError()
+ if(data != NULL)
+ {
+ sockaddr_storage *addr = (sockaddr_storage*)data;
+ jobject addressObj = newInetSocketAddress(env, *addr);
+ env->CallVoidMethod(ref->eventListener, onNetworkErrorMethod, addressObj);
+ }
+ }
+ case ZT1_EVENT_TRACE:
+ {
+ // call onTrace()
+ if(data != NULL)
+ {
+ const char* message = (const char*)data;
+ jstring messageStr = env->NewStringUTF(message);
+ env->CallVoidMethod(ref->eventListener, onTraceMethod, messageStr);
+ }
+ }
+ break;
+ }
+ }
+
+ long DataStoreGetFunction(ZT1_Node *node,void *userData,
+ const char *objectName,
+ void *buffer,
+ unsigned long bufferSize,
+ unsigned long bufferIndex,
+ unsigned long *out_objectSize)
+ {
+ JniRef *ref = (JniRef*)userData;
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+ jclass dataStoreGetClass = env->GetObjectClass(ref->dataStoreGetListener);
+ if(dataStoreGetClass == NULL)
+ {
+ LOGE("Couldn't find class for DataStoreGetListener instance");
+ return -2;
+ }
+
+ jmethodID dataStoreGetCallbackMethod = cache.findMethod(
+ dataStoreGetClass,
+ "onDataStoreGet",
+ "(Ljava/lang/String;[BJ[J)J");
+ if(dataStoreGetCallbackMethod == NULL)
+ {
+ LOGE("Couldn't find onDataStoreGet method");
+ return -2;
+ }
+
+ jstring nameStr = env->NewStringUTF(objectName);
+ if(nameStr == NULL)
+ {
+ LOGE("Error creating name string object");
+ return -2; // out of memory
+ }
+
+ jbyteArray bufferObj = env->NewByteArray(bufferSize);
+ if(bufferObj == NULL)
+ {
+ LOGE("Error creating byte[] buffer of size: %lu", bufferSize);
+ return -2;
+ }
+
+ jlongArray objectSizeObj = env->NewLongArray(1);
+ if(objectSizeObj == NULL)
+ {
+ LOGE("Error creating long[1] array for actual object size");
+ return -2; // couldn't create long[1] array
+ }
+
+ LOGD("Calling onDataStoreGet(%s, %p, %lu, %p)",
+ objectName, buffer, bufferIndex, objectSizeObj);
+
+ long retval = (long)env->CallLongMethod(
+ ref->dataStoreGetListener, dataStoreGetCallbackMethod,
+ nameStr, bufferObj, (jlong)bufferIndex, objectSizeObj);
+
+ if(retval > 0)
+ {
+ env->GetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer);
+ env->GetLongArrayRegion(objectSizeObj, 0, 1, (jlong*)out_objectSize);
+ env->ReleaseByteArrayElements(bufferObj, (jbyte*)buffer, 0);
+ env->ReleaseLongArrayElements(objectSizeObj, (jlong*)out_objectSize, 0);
+ }
+
+ LOGI("Out Object Size: %lu", *out_objectSize);
+
+ return retval;
+ }
+
+ int DataStorePutFunction(ZT1_Node *node,void *userData,
+ const char *objectName,
+ const void *buffer,
+ unsigned long bufferSize,
+ int secure)
+ {
+ JniRef *ref = (JniRef*)userData;
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+
+ jclass dataStorePutClass = env->GetObjectClass(ref->dataStorePutListener);
+ if(dataStorePutClass == NULL)
+ {
+ LOGE("Couldn't find class for DataStorePutListener instance");
+ return -1;
+ }
+
+ jmethodID dataStorePutCallbackMethod = cache.findMethod(
+ dataStorePutClass,
+ "onDataStorePut",
+ "(Ljava/lang/String;[BZ)I");
+ if(dataStorePutCallbackMethod == NULL)
+ {
+ LOGE("Couldn't find onDataStorePut method");
+ return -2;
+ }
+
+ jmethodID deleteMethod = cache.findMethod(dataStorePutClass,
+ "onDelete", "(Ljava/lang/String;)I");
+ if(deleteMethod == NULL)
+ {
+ LOGE("Couldn't find onDelete method");
+ return -3;
+ }
+
+ jstring nameStr = env->NewStringUTF(objectName);
+
+ if(buffer == NULL)
+ {
+ // delete operation
+ return env->CallIntMethod(
+ ref->dataStorePutListener, deleteMethod, nameStr);
+ }
+ else
+ {
+ // set operation
+ jbyteArray bufferObj = env->NewByteArray(bufferSize);
+ env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer);
+ bool bsecure = secure != 0;
+
+
+ return env->CallIntMethod(ref->dataStorePutListener,
+ dataStorePutCallbackMethod,
+ nameStr, bufferObj, bsecure);
+ }
+ }
+
+ int WirePacketSendFunction(ZT1_Node *node,void *userData,\
+ const struct sockaddr_storage *address,
+ const void *buffer,
+ unsigned int bufferSize)
+ {
+ JniRef *ref = (JniRef*)userData;
+ assert(ref->node == node);
+
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+
+ jclass packetSenderClass = env->GetObjectClass(ref->packetSender);
+ if(packetSenderClass == NULL)
+ {
+ LOGE("Couldn't find class for PacketSender instance");
+ return -1;
+ }
+
+ jmethodID packetSenderCallbackMethod = cache.findMethod(packetSenderClass,
+ "onSendPacketRequested", "(Ljava/net/InetSocketAddress;[B)I");
+ if(packetSenderCallbackMethod == NULL)
+ {
+ LOGE("Couldn't find onSendPacketRequested method");
+ return -2;
+ }
+
+ jobject addressObj = newInetSocketAddress(env, *address);
+ jbyteArray bufferObj = env->NewByteArray(bufferSize);
+ env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer);
+ return env->CallIntMethod(ref->packetSender, packetSenderCallbackMethod, addressObj, bufferObj);
+ }
+
+ typedef std::map<uint64_t, JniRef*> NodeMap;
+ static NodeMap nodeMap;
+
+ ZT1_Node* findNode(uint64_t nodeId)
+ {
+ NodeMap::iterator found = nodeMap.find(nodeId);
+ if(found != nodeMap.end())
+ {
+ JniRef *ref = found->second;
+ return ref->node;
+ }
+ return NULL;
+ }
+}
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
+{
+ cache.setJavaVM(vm);
+ return JNI_VERSION_1_6;
+}
+
+JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved)
+{
+ cache.clearCache();
+}
+
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: node_init
+ * Signature: (J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init(
+ JNIEnv *env, jobject obj, jlong now)
+{
+ LOGD("Creating ZT1_Node struct");
+ jobject resultObject = createResultObject(env, ZT1_RESULT_OK);
+
+ ZT1_Node *node;
+ JniRef *ref = new JniRef;
+ ref->id = (uint64_t)now;
+ env->GetJavaVM(&ref->jvm);
+
+ jclass cls = env->GetObjectClass(obj);
+ jfieldID fid = cache.findField(
+ cls, "getListener", "Lcom/zerotier/sdk/DataStoreGetListener;");
+
+ if(fid == NULL)
+ {
+ return NULL; // exception already thrown
+ }
+
+ jobject tmp = env->GetObjectField(obj, fid);
+ if(tmp == NULL)
+ {
+ return NULL;
+ }
+ ref->dataStoreGetListener = env->NewGlobalRef(tmp);
+
+ fid = cache.findField(
+ cls, "putListener", "Lcom/zerotier/sdk/DataStorePutListener;");
+
+ if(fid == NULL)
+ {
+ return NULL; // exception already thrown
+ }
+
+ tmp = env->GetObjectField(obj, fid);
+ if(tmp == NULL)
+ {
+ return NULL;
+ }
+ ref->dataStorePutListener = env->NewGlobalRef(tmp);
+
+ fid = cache.findField(
+ cls, "sender", "Lcom/zerotier/sdk/PacketSender;");
+ if(fid == NULL)
+ {
+ return NULL; // exception already thrown
+ }
+
+ tmp = env->GetObjectField(obj, fid);
+ if(tmp == NULL)
+ {
+ return NULL;
+ }
+ ref->packetSender = env->NewGlobalRef(tmp);
+
+ fid = cache.findField(
+ cls, "frameListener", "Lcom/zerotier/sdk/VirtualNetworkFrameListener;");
+ if(fid == NULL)
+ {
+ return NULL; // exception already thrown
+ }
+
+ tmp = env->GetObjectField(obj, fid);
+ if(tmp == NULL)
+ {
+ return NULL;
+ }
+ ref->frameListener = env->NewGlobalRef(tmp);
+
+ fid = cache.findField(
+ cls, "configListener", "Lcom/zerotier/sdk/VirtualNetworkConfigListener;");
+ if(fid == NULL)
+ {
+ return NULL; // exception already thrown
+ }
+
+ tmp = env->GetObjectField(obj, fid);
+ if(tmp == NULL)
+ {
+ return NULL;
+ }
+ ref->configListener = env->NewGlobalRef(tmp);
+
+ fid = cache.findField(
+ cls, "eventListener", "Lcom/zerotier/sdk/EventListener;");
+ if(fid == NULL)
+ {
+ return NULL;
+ }
+
+ tmp = env->GetObjectField(obj, fid);
+ if(tmp == NULL)
+ {
+ return NULL;
+ }
+ ref->eventListener = env->NewGlobalRef(tmp);
+
+ ZT1_ResultCode rc = ZT1_Node_new(
+ &node,
+ ref,
+ (uint64_t)now,
+ &DataStoreGetFunction,
+ &DataStorePutFunction,
+ &WirePacketSendFunction,
+ &VirtualNetworkFrameFunctionCallback,
+ &VirtualNetworkConfigFunctionCallback,
+ &EventCallback);
+
+ if(rc != ZT1_RESULT_OK)
+ {
+ LOGE("Error creating Node: %d", rc);
+ resultObject = createResultObject(env, rc);
+ if(node)
+ {
+ ZT1_Node_delete(node);
+ node = NULL;
+ }
+ delete ref;
+ ref = NULL;
+ return resultObject;
+ }
+
+ ref->node = node;
+ nodeMap.insert(std::make_pair(ref->id, ref));
+
+ return resultObject;
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: node_delete
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete(
+ JNIEnv *env, jobject obj, jlong id)
+{
+ LOGD("Destroying ZT1_Node struct");
+ uint64_t nodeId = (uint64_t)id;
+
+ NodeMap::iterator found = nodeMap.find(nodeId);
+ if(found != nodeMap.end())
+ {
+ JniRef *ref = found->second;
+ nodeMap.erase(found);
+
+ ZT1_Node_delete(ref->node);
+
+ delete ref;
+ ref = NULL;
+ }
+ else
+ {
+ LOGE("Attempted to delete a node that doesn't exist!");
+ }
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: processVirtualNetworkFrame
+ * Signature: (JJJJJII[B[J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_now,
+ jlong in_nwid,
+ jlong in_sourceMac,
+ jlong in_destMac,
+ jint in_etherType,
+ jint in_vlanId,
+ jbyteArray in_frameData,
+ jlongArray out_nextBackgroundTaskDeadline)
+{
+ uint64_t nodeId = (uint64_t) id;
+
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ unsigned int nbtd_len = env->GetArrayLength(out_nextBackgroundTaskDeadline);
+ if(nbtd_len < 1)
+ {
+ // array for next background task length has 0 elements!
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ uint64_t now = (uint64_t)in_now;
+ uint64_t nwid = (uint64_t)in_nwid;
+ uint64_t sourceMac = (uint64_t)in_sourceMac;
+ uint64_t destMac = (uint64_t)in_destMac;
+ unsigned int etherType = (unsigned int)in_etherType;
+ unsigned int vlanId = (unsigned int)in_vlanId;
+
+ unsigned int frameLength = env->GetArrayLength(in_frameData);
+ jbyte *frameData =env->GetByteArrayElements(in_frameData, NULL);
+
+ uint64_t nextBackgroundTaskDeadline = 0;
+
+ ZT1_ResultCode rc = ZT1_Node_processVirtualNetworkFrame(
+ node,
+ now,
+ nwid,
+ sourceMac,
+ destMac,
+ etherType,
+ vlanId,
+ (const void*)frameData,
+ frameLength,
+ &nextBackgroundTaskDeadline);
+
+ jlong *outDeadline = env->GetLongArrayElements(out_nextBackgroundTaskDeadline, NULL);
+ outDeadline[0] = (jlong)nextBackgroundTaskDeadline;
+ env->ReleaseLongArrayElements(out_nextBackgroundTaskDeadline, outDeadline, 0);
+
+ env->ReleaseByteArrayElements(in_frameData, frameData, 0);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: processWirePacket
+ * Signature: (JJLjava/net/InetSocketAddress;I[B[J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_now,
+ jobject in_remoteAddress,
+ jbyteArray in_packetData,
+ jlongArray out_nextBackgroundTaskDeadline)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ unsigned int nbtd_len = env->GetArrayLength(out_nextBackgroundTaskDeadline);
+ if(nbtd_len < 1)
+ {
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ uint64_t now = (uint64_t)in_now;
+
+ // get the java.net.InetSocketAddress class and getAddress() method
+ jclass inetAddressClass = cache.findClass("java/net/InetAddress");
+ if(inetAddressClass == NULL)
+ {
+ // can't find java.net.InetAddress
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ jmethodID getAddressMethod = cache.findMethod(
+ inetAddressClass, "getAddress", "()[B");
+ if(getAddressMethod == NULL)
+ {
+ // cant find InetAddress.getAddres()
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ jclass InetSocketAddressClass = cache.findClass("java/net/InetSocketAddress");
+ if(InetSocketAddressClass == NULL)
+ {
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ jmethodID inetSockGetAddressMethod = cache.findMethod(
+ InetSocketAddressClass, "getAddress", "()Ljava/net/InetAddress;");
+
+ jobject addrObject = env->CallObjectMethod(in_remoteAddress, inetSockGetAddressMethod);
+
+ if(addrObject == NULL)
+ {
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ // Call InetAddress.getAddress()
+ jbyteArray addressArray = (jbyteArray)env->CallObjectMethod(addrObject, getAddressMethod);
+ if(addressArray == NULL)
+ {
+ // unable to call getAddress()
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ unsigned int addrSize = env->GetArrayLength(addressArray);
+ // get the address bytes
+ jbyte *addr = env->GetByteArrayElements(addressArray, NULL);
+
+
+ sockaddr_storage remoteAddress = {};
+
+ if(addrSize == 16)
+ {
+ // IPV6 address
+ sockaddr_in6 ipv6 = {};
+ ipv6.sin6_family = AF_INET6;
+ memcpy(ipv6.sin6_addr.s6_addr, addr, 16);
+ memcpy(&remoteAddress, &ipv6, sizeof(sockaddr_in6));
+ }
+ else if(addrSize == 4)
+ {
+ // IPV4 address
+ sockaddr_in ipv4 = {};
+ ipv4.sin_family = AF_INET;
+ memcpy(&ipv4.sin_addr, addr, 4);
+ memcpy(&remoteAddress, &ipv4, sizeof(sockaddr_in));
+ }
+ else
+ {
+ // unknown address type
+ env->ReleaseByteArrayElements(addressArray, addr, 0);
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+
+ unsigned int packetLength = env->GetArrayLength(in_packetData);
+ jbyte *packetData = env->GetByteArrayElements(in_packetData, NULL);
+
+ uint64_t nextBackgroundTaskDeadline = 0;
+
+ ZT1_ResultCode rc = ZT1_Node_processWirePacket(
+ node,
+ now,
+ &remoteAddress,
+ packetData,
+ packetLength,
+ &nextBackgroundTaskDeadline);
+
+ jlong *outDeadline = env->GetLongArrayElements(out_nextBackgroundTaskDeadline, NULL);
+ outDeadline[0] = (jlong)nextBackgroundTaskDeadline;
+ env->ReleaseLongArrayElements(out_nextBackgroundTaskDeadline, outDeadline, 0);
+
+ env->ReleaseByteArrayElements(addressArray, addr, 0);
+ env->ReleaseByteArrayElements(in_packetData, packetData, 0);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: processBackgroundTasks
+ * Signature: (JJ[J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_now,
+ jlongArray out_nextBackgroundTaskDeadline)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ unsigned int nbtd_len = env->GetArrayLength(out_nextBackgroundTaskDeadline);
+ if(nbtd_len < 1)
+ {
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ uint64_t now = (uint64_t)in_now;
+ uint64_t nextBackgroundTaskDeadline = 0;
+
+ ZT1_ResultCode rc = ZT1_Node_processBackgroundTasks(node, now, &nextBackgroundTaskDeadline);
+
+ jlong *outDeadline = env->GetLongArrayElements(out_nextBackgroundTaskDeadline, NULL);
+ outDeadline[0] = (jlong)nextBackgroundTaskDeadline;
+ env->ReleaseLongArrayElements(out_nextBackgroundTaskDeadline, outDeadline, 0);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: join
+ * Signature: (JJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_join(
+ JNIEnv *env, jobject obj, jlong id, jlong in_nwid)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ uint64_t nwid = (uint64_t)in_nwid;
+
+ ZT1_ResultCode rc = ZT1_Node_join(node, nwid);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: leave
+ * Signature: (JJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave(
+ JNIEnv *env, jobject obj, jlong id, jlong in_nwid)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ uint64_t nwid = (uint64_t)in_nwid;
+
+ ZT1_ResultCode rc = ZT1_Node_leave(node, nwid);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: multicastSubscribe
+ * Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_nwid,
+ jlong in_multicastGroup,
+ jlong in_multicastAdi)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ uint64_t nwid = (uint64_t)in_nwid;
+ uint64_t multicastGroup = (uint64_t)in_multicastGroup;
+ unsigned long multicastAdi = (unsigned long)in_multicastAdi;
+
+ ZT1_ResultCode rc = ZT1_Node_multicastSubscribe(
+ node, nwid, multicastGroup, multicastAdi);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: multicastUnsubscribe
+ * Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_nwid,
+ jlong in_multicastGroup,
+ jlong in_multicastAdi)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ uint64_t nwid = (uint64_t)in_nwid;
+ uint64_t multicastGroup = (uint64_t)in_multicastGroup;
+ unsigned long multicastAdi = (unsigned long)in_multicastAdi;
+
+ ZT1_ResultCode rc = ZT1_Node_multicastUnsubscribe(
+ node, nwid, multicastGroup, multicastAdi);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: address
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_com_zerotier_sdk_Node_address(
+ JNIEnv *env , jobject obj, jlong id)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return 0;
+ }
+
+ uint64_t address = ZT1_Node_address(node);
+ return (jlong)address;
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: status
+ * Signature: (J)Lcom/zerotier/sdk/NodeStatus;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status
+ (JNIEnv *env, jobject obj, jlong id)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return 0;
+ }
+
+ jclass nodeStatusClass = NULL;
+ jmethodID nodeStatusConstructor = NULL;
+
+ // create a com.zerotier.sdk.NodeStatus object
+ nodeStatusClass = cache.findClass("com/zerotier/sdk/NodeStatus");
+ if(nodeStatusClass == NULL)
+ {
+ return NULL;
+ }
+
+ nodeStatusConstructor = cache.findMethod(
+ nodeStatusClass, "<init>", "()V");
+ if(nodeStatusConstructor == NULL)
+ {
+ return NULL;
+ }
+
+ jobject nodeStatusObj = env->NewObject(nodeStatusClass, nodeStatusConstructor);
+ if(nodeStatusObj == NULL)
+ {
+ return NULL;
+ }
+
+ ZT1_NodeStatus nodeStatus;
+ ZT1_Node_status(node, &nodeStatus);
+
+ jfieldID addressField = NULL;
+ jfieldID publicIdentityField = NULL;
+ jfieldID secretIdentityField = NULL;
+ jfieldID onlineField = NULL;
+
+ addressField = cache.findField(nodeStatusClass, "address", "J");
+ if(addressField == NULL)
+ {
+ return NULL;
+ }
+
+ publicIdentityField = cache.findField(nodeStatusClass, "publicIdentity", "Ljava/lang/String;");
+ if(publicIdentityField == NULL)
+ {
+ return NULL;
+ }
+
+ secretIdentityField = cache.findField(nodeStatusClass, "secretIdentity", "Ljava/lang/String;");
+ if(secretIdentityField == NULL)
+ {
+ return NULL;
+ }
+
+ onlineField = cache.findField(nodeStatusClass, "online", "Z");
+ if(onlineField == NULL)
+ {
+ return NULL;
+ }
+
+ env->SetLongField(nodeStatusObj, addressField, nodeStatus.address);
+
+ jstring pubIdentStr = env->NewStringUTF(nodeStatus.publicIdentity);
+ if(pubIdentStr == NULL)
+ {
+ return NULL; // out of memory
+ }
+ env->SetObjectField(nodeStatusObj, publicIdentityField, pubIdentStr);
+
+ jstring secIdentStr = env->NewStringUTF(nodeStatus.secretIdentity);
+ if(secIdentStr == NULL)
+ {
+ return NULL; // out of memory
+ }
+ env->SetObjectField(nodeStatusObj, secretIdentityField, secIdentStr);
+
+ env->SetBooleanField(nodeStatusObj, onlineField, nodeStatus.online);
+
+ return nodeStatusObj;
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: networkConfig
+ * Signature: (J)Lcom/zerotier/sdk/VirtualNetworkConfig;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networkConfig(
+ JNIEnv *env, jobject obj, jlong id, jlong nwid)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return 0;
+ }
+
+ ZT1_VirtualNetworkConfig *vnetConfig = ZT1_Node_networkConfig(node, nwid);
+
+ jobject vnetConfigObject = newNetworkConfig(env, *vnetConfig);
+
+ ZT1_Node_freeQueryResult(node, vnetConfig);
+
+ return vnetConfigObject;
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: version
+ * Signature: (J)Lcom/zerotier/sdk/Version;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_version(
+ JNIEnv *env, jobject obj)
+{
+ int major = 0;
+ int minor = 0;
+ int revision = 0;
+ unsigned long featureFlags = 0;
+
+ ZT1_version(&major, &minor, &revision, &featureFlags);
+
+ return newVersion(env, major, minor, revision, featureFlags);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: peers
+ * Signature: (J)Ljava/util/ArrayList;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_peers(
+ JNIEnv *env, jobject obj, jlong id)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return 0;
+ }
+
+ ZT1_PeerList *peerList = ZT1_Node_peers(node);
+
+ if(peerList == NULL)
+ {
+ return NULL;
+ }
+
+ jobject peerListObject = newArrayList(env);
+ if(peerListObject == NULL)
+ {
+ ZT1_Node_freeQueryResult(node, peerList);
+ return NULL;
+ }
+
+ for(unsigned int i = 0; i < peerList->peerCount; ++i)
+ {
+ jobject peerObj = newPeer(env, peerList->peers[i]);
+ appendItemToArrayList(env, peerListObject, peerObj);
+ }
+
+ ZT1_Node_freeQueryResult(node, peerList);
+ peerList = NULL;
+
+ return peerListObject;
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: networks
+ * Signature: (J)Ljava/util/ArrayList;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networks(
+ JNIEnv *env, jobject obj, jlong id)
+{
+ uint64_t nodeId = (uint64_t) id;
+ ZT1_Node *node = findNode(nodeId);
+ if(node == NULL)
+ {
+ // cannot find valid node. We should never get here.
+ return 0;
+ }
+
+ ZT1_VirtualNetworkList *networkList = ZT1_Node_networks(node);
+ if(networkList == NULL)
+ {
+ return NULL;
+ }
+
+ jobject networkListObject = newArrayList(env);
+ if(networkListObject == NULL)
+ {
+ ZT1_Node_freeQueryResult(node, networkList);
+ return NULL;
+ }
+
+ for(unsigned int i = 0; i < networkList->networkCount; ++i)
+ {
+ jobject networkObject = newNetworkConfig(env, networkList->networks[i]);
+ appendItemToArrayList(env, networkListObject, networkObject);
+ }
+
+ ZT1_Node_freeQueryResult(node, networkList);
+
+ return networkListObject;
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif \ No newline at end of file
diff --git a/java/jni/com_zerotierone_sdk_Node.h b/java/jni/com_zerotierone_sdk_Node.h
new file mode 100644
index 00000000..6b41b02a
--- /dev/null
+++ b/java/jni/com_zerotierone_sdk_Node.h
@@ -0,0 +1,133 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_zerotier_sdk_Node */
+
+#ifndef _Included_com_zerotierone_sdk_Node
+#define _Included_com_zerotierone_sdk_Node
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: node_init
+ * Signature: (J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: node_delete
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: processVirtualNetworkFrame
+ * Signature: (JJJJJII[B[J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame
+ (JNIEnv *, jobject, jlong, jlong, jlong, jlong, jlong, jint, jint, jbyteArray, jlongArray);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: processWirePacket
+ * Signature: (JJLjava/net/InetSockAddress;[B[J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket
+ (JNIEnv *, jobject, jlong, jlong, jobject, jbyteArray, jlongArray);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: processBackgroundTasks
+ * Signature: (JJ[J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks
+ (JNIEnv *, jobject, jlong, jlong, jlongArray);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: join
+ * Signature: (JJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_join
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: leave
+ * Signature: (JJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: multicastSubscribe
+ * Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe
+ (JNIEnv *, jobject, jlong, jlong, jlong, jlong);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: multicastUnsubscribe
+ * Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe
+ (JNIEnv *, jobject, jlong, jlong, jlong, jlong);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: address
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_com_zerotier_sdk_Node_address
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: status
+ * Signature: (J)Lcom/zerotier/sdk/NodeStatus;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: networkConfig
+ * Signature: (JJ)Lcom/zerotier/sdk/VirtualNetworkConfig;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networkConfig
+ (JNIEnv *, jobject, jlong, jlong);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: version
+ * Signature: ()Lcom/zerotier/sdk/Version;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_version
+ (JNIEnv *, jobject);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: peers
+ * Signature: (J)Ljava/util/ArrayList;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_peers
+ (JNIEnv *, jobject, jlong);
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: networks
+ * Signature: (J)Ljava/util/ArrayList;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networks
+ (JNIEnv *, jobject, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/java/src/com/zerotier/one/AndroidFileProvider.java b/java/src/com/zerotier/one/AndroidFileProvider.java
new file mode 100644
index 00000000..0988f9df
--- /dev/null
+++ b/java/src/com/zerotier/one/AndroidFileProvider.java
@@ -0,0 +1,43 @@
+package com.zerotier.one;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import android.content.Context;
+import android.util.Log;
+
+public class AndroidFileProvider implements DataStoreFileProvider {
+ private static final String TAG = "AndroidFileProvider";
+
+ Context _ctx;
+
+ public AndroidFileProvider(Context ctx) {
+ this._ctx = ctx;
+ }
+
+ @Override
+ public FileInputStream getInputFileStream(String name)
+ throws FileNotFoundException {
+ Log.d(TAG, "Returning FileInputStream for: " + name);
+ return _ctx.openFileInput(name);
+ }
+
+ @Override
+ public FileOutputStream getOutputFileStream(String name)
+ throws FileNotFoundException {
+ Log.d(TAG, "Returning FileOutputStream for: " + name);
+ return _ctx.openFileOutput(name, Context.MODE_PRIVATE);
+ }
+
+ @Override
+ public void deleteFile(String name) throws IOException {
+ boolean success = _ctx.deleteFile(name);
+ if(!success)
+ {
+ throw new IOException("Unable to delete file.");
+ }
+ }
+
+}
diff --git a/java/src/com/zerotier/one/DataStore.java b/java/src/com/zerotier/one/DataStore.java
new file mode 100644
index 00000000..d15b2d3d
--- /dev/null
+++ b/java/src/com/zerotier/one/DataStore.java
@@ -0,0 +1,73 @@
+package com.zerotier.one;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import com.zerotier.sdk.DataStoreGetListener;
+import com.zerotier.sdk.DataStorePutListener;
+
+public class DataStore implements DataStoreGetListener, DataStorePutListener {
+
+ private DataStoreFileProvider _provider;
+
+ public DataStore(DataStoreFileProvider provider) {
+ this._provider = provider;
+ }
+
+ @Override
+ public int onDataStorePut(String name, byte[] buffer, boolean secure) {
+ System.out.println("Writing File: " + name);
+ try {
+ FileOutputStream fos = _provider.getOutputFileStream(name);
+ fos.write(buffer);
+ fos.close();
+ return 0;
+ } catch (FileNotFoundException fnf) {
+ fnf.printStackTrace();
+ return -1;
+ } catch (IOException io) {
+ io.printStackTrace();
+ return -2;
+ }
+ }
+
+ @Override
+ public int onDelete(String name) {
+ System.out.println("Deleting File: " + name);
+ try {
+ _provider.deleteFile(name);
+ return 0;
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ return -1;
+ }
+ }
+
+ @Override
+ public long onDataStoreGet(String name, byte[] out_buffer,
+ long bufferIndex, long[] out_objectSize) {
+ System.out.println("Reading File: " + name);
+ try {
+ FileInputStream fin = _provider.getInputFileStream(name);
+ out_objectSize[0] = fin.getChannel().size();
+ if(bufferIndex > 0)
+ {
+ fin.skip(bufferIndex);
+ }
+ int read = fin.read(out_buffer);
+ fin.close();
+ return read;
+ } catch (FileNotFoundException fnf) {
+ // Can't read a file that doesn't exist!
+ out_objectSize[0] = 0;
+ return 0;
+ } catch (IOException io) {
+ io.printStackTrace();
+ return -2;
+ }
+ }
+
+
+}
diff --git a/java/src/com/zerotier/one/DataStoreFileProvider.java b/java/src/com/zerotier/one/DataStoreFileProvider.java
new file mode 100644
index 00000000..ffe078eb
--- /dev/null
+++ b/java/src/com/zerotier/one/DataStoreFileProvider.java
@@ -0,0 +1,12 @@
+package com.zerotier.one;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public interface DataStoreFileProvider {
+ FileInputStream getInputFileStream(String name) throws FileNotFoundException;
+ FileOutputStream getOutputFileStream(String name) throws FileNotFoundException;
+ void deleteFile(String name) throws IOException;
+}
diff --git a/java/src/com/zerotier/one/JavaFileProvider.java b/java/src/com/zerotier/one/JavaFileProvider.java
new file mode 100644
index 00000000..41889e2f
--- /dev/null
+++ b/java/src/com/zerotier/one/JavaFileProvider.java
@@ -0,0 +1,46 @@
+package com.zerotier.one;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class JavaFileProvider implements DataStoreFileProvider {
+ private String _path;
+
+ public JavaFileProvider(String path) {
+ this._path = path;
+ }
+
+ @Override
+ public FileInputStream getInputFileStream(String name)
+ throws FileNotFoundException {
+ File f = new File(_path + File.separator + name);
+ return new FileInputStream(f);
+ }
+
+ @Override
+ public FileOutputStream getOutputFileStream(String name)
+ throws FileNotFoundException {
+ File f = new File(_path + File.separator + name);
+ if(!f.exists())
+ {
+ try {
+ f.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return new FileOutputStream(f);
+ }
+
+ @Override
+ public void deleteFile(String name) throws IOException {
+ File f = new File(_path + File.separator + name);
+ boolean success = f.delete();
+ if(!success) {
+ throw new IOException("Unable to delete file: " + _path + File.pathSeparator + name);
+ }
+ }
+}
diff --git a/java/src/com/zerotier/one/OneService.java b/java/src/com/zerotier/one/OneService.java
new file mode 100644
index 00000000..1d3e34c8
--- /dev/null
+++ b/java/src/com/zerotier/one/OneService.java
@@ -0,0 +1,204 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+
+package com.zerotier.one;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+
+import com.zerotier.sdk.Event;
+import com.zerotier.sdk.EventListener;
+import com.zerotier.sdk.Node;
+import com.zerotier.sdk.PacketSender;
+import com.zerotier.sdk.ResultCode;
+import com.zerotier.sdk.Version;
+import com.zerotier.sdk.VirtualNetworkConfig;
+import com.zerotier.sdk.VirtualNetworkConfigListener;
+import com.zerotier.sdk.VirtualNetworkConfigOperation;
+import com.zerotier.sdk.VirtualNetworkFrameListener;
+
+public class OneService extends Thread implements Runnable, PacketSender,
+ EventListener, VirtualNetworkConfigListener,
+ VirtualNetworkFrameListener {
+ private Node _node;
+ private int _port;
+
+ private DatagramSocket _udpSocket;
+ private ServerSocket _tcpSocket;
+ private DataStore _ds;
+ private long _nextBackgroundTaskDeadline = 0;
+
+ private final Thread _udpReceiveThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ long[] bgtask = new long[1];
+ byte[] buffer = new byte[16384];
+ while(true) {
+
+ bgtask[0] = 0;
+ DatagramPacket p = new DatagramPacket(buffer, buffer.length);
+
+ try {
+ _udpSocket.receive(p);
+ if(p.getLength() > 0)
+ {
+ System.out.println("Got Data From: " + p.getAddress().toString() +":" + p.getPort());
+
+ _node.processWirePacket(System.currentTimeMillis(), new InetSocketAddress(p.getAddress(), p.getPort()), p.getData(), bgtask);
+ _nextBackgroundTaskDeadline = bgtask[0];
+ }
+ } catch (SocketTimeoutException e) {}
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ };
+
+
+ public OneService(DataStoreFileProvider prov, int port) {
+ this._port = port;
+ this._ds = new DataStore(prov);
+
+ try {
+ _udpSocket = new DatagramSocket(_port);
+ _udpSocket.setSoTimeout(100);
+ _tcpSocket = new ServerSocket();
+ _tcpSocket.bind(new InetSocketAddress("127.0.0.1", _port));
+ } catch (SocketException e) {
+ e.printStackTrace();
+ return;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ _udpReceiveThread.start();
+
+ _node = new Node(
+ System.currentTimeMillis(),
+ _ds,
+ _ds,
+ this,
+ this,
+ this,
+ this);
+ }
+
+ @Override
+ public void run() {
+ if(_node == null)
+ return;
+
+ while(true) {
+ try {
+
+ long dl = _nextBackgroundTaskDeadline;
+ long now = System.currentTimeMillis();
+
+ if (dl <= now) {
+ long[] returnDeadline = {0};
+ ResultCode rc = _node.processBackgroundTasks(now, returnDeadline);
+ _nextBackgroundTaskDeadline = returnDeadline[0];
+
+ if(rc != ResultCode.RESULT_OK) {
+ System.out.println(rc.toString());
+ }
+ }
+
+ long delay = (dl > now) ? (dl - now) : 100;
+ Thread.sleep(delay);
+
+ } catch (Exception ex) {
+ System.out.println("Exception in run loop: " + ex.getMessage());
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public int onSendPacketRequested(InetSocketAddress addr, byte[] packetData) {
+ System.out.println("onSendPacketRequested to: " + addr.getHostString() +":"+ addr.getPort() + " ");
+
+ if(_udpSocket == null)
+ return -1;
+ try {
+ DatagramPacket p = new DatagramPacket(packetData, packetData.length, addr);
+ _udpSocket.send(p);
+ System.out.println("Sent");
+ } catch (Exception e) {
+ System.out.println("Error sending datagram: " + e.getMessage());
+ return -1;
+ }
+ return 0;
+ }
+
+ @Override
+ public void onVirtualNetworkFrame(long nwid, long srcMac, long destMac,
+ long etherType, long vlanId, byte[] frameData) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public int onNetworkConfigurationUpdated(long nwid,
+ VirtualNetworkConfigOperation op, VirtualNetworkConfig config) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public void onEvent(Event event) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onNetworkError(Event event, InetSocketAddress source) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onOutOfDate(Version newVersion) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onTrace(String message) {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/java/src/com/zerotier/sdk/DataStoreGetListener.java b/java/src/com/zerotier/sdk/DataStoreGetListener.java
new file mode 100644
index 00000000..b525be63
--- /dev/null
+++ b/java/src/com/zerotier/sdk/DataStoreGetListener.java
@@ -0,0 +1,58 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+package com.zerotier.sdk;
+
+public interface DataStoreGetListener {
+
+ /**
+ * Function to get an object from the data store
+ *
+ * <p>Object names can contain forward slash (/) path separators. They will
+ * never contain .. or backslash (\), so this is safe to map as a Unix-style
+ * path if the underlying storage permits. For security reasons we recommend
+ * returning errors if .. or \ are used.</p>
+ *
+ * <p>The function must return the actual number of bytes read. If the object
+ * doesn't exist, it should return -1. -2 should be returned on other errors
+ * such as errors accessing underlying storage.</p>
+ *
+ * <p>If the read doesn't fit in the buffer, the max number of bytes should be
+ * read. The caller may call the function multiple times to read the whole
+ * object.</p>
+ *
+ * @param name Name of the object in the data store
+ * @param out_buffer buffer to put the object in
+ * @param bufferIndex index in the object to start reading
+ * @param out_objectSize long[1] to be set to the actual size of the object if it exists.
+ * @return the actual number of bytes read.
+ */
+ public long onDataStoreGet(
+ String name,
+ byte[] out_buffer,
+ long bufferIndex,
+ long[] out_objectSize);
+}
diff --git a/java/src/com/zerotier/sdk/DataStorePutListener.java b/java/src/com/zerotier/sdk/DataStorePutListener.java
new file mode 100644
index 00000000..77e55027
--- /dev/null
+++ b/java/src/com/zerotier/sdk/DataStorePutListener.java
@@ -0,0 +1,59 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+package com.zerotier.sdk;
+
+public interface DataStorePutListener {
+
+ /**
+ * Function to store an object in the data store
+ *
+ * <p>If secure is true, the file should be set readable and writable only
+ * to the user running ZeroTier One. What this means is platform-specific.</p>
+ *
+ * <p>Name semantics are the same as {@link DataStoreGetListener}. This must return
+ * zero on success. You can return any OS-specific error code on failure, as these
+ * may be visible in logs or error messages and might aid in debugging.</p>
+ *
+ * @param name Object name
+ * @param buffer data to store
+ * @param secure set to user read/write only.
+ * @return 0 on success.
+ */
+ public int onDataStorePut(
+ String name,
+ byte[] buffer,
+ boolean secure);
+
+ /**
+ * Function to delete an object from the data store
+ *
+ * @param name Object name
+ * @return 0 on success.
+ */
+ public int onDelete(
+ String name);
+}
diff --git a/java/src/com/zerotier/sdk/Event.java b/java/src/com/zerotier/sdk/Event.java
new file mode 100644
index 00000000..d315990b
--- /dev/null
+++ b/java/src/com/zerotier/sdk/Event.java
@@ -0,0 +1,123 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+public enum Event {
+ /**
+ * Node has been initialized
+ *
+ * This is the first event generated, and is always sent. It may occur
+ * before Node's constructor returns.
+ */
+ EVENT_UP,
+
+ /**
+ * Node is offline -- network does not seem to be reachable by any available strategy
+ */
+ EVENT_OFFLINE,
+
+ /**
+ * Node is online -- at least one upstream node appears reachable
+ *
+ * Meta-data: none
+ */
+ EVENT_ONLINE,
+
+ /**
+ * Node is shutting down
+ *
+ * <p>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.</p>
+ */
+ EVENT_DOWN,
+
+ /**
+ * Your identity has collided with another node's ZeroTier address
+ *
+ * <p>This happens if two different public keys both hash (via the algorithm
+ * in Identity::generate()) to the same 40-bit ZeroTier address.</p>
+ *
+ * <p>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.</p>
+ *
+ * <p>This is reported as an event rather than a return code since it's
+ * detected asynchronously via error messages from authoritative nodes.</p>
+ *
+ * <p>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.</p>
+ *
+ * <p>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.</p>
+ *
+ * <p>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?</p>
+ */
+ EVENT_FATAL_ERROR_IDENTITY_COLLISION,
+
+ /**
+ * A more recent version was observed on the network
+ *
+ * <p>Right now this is only triggered if a hub or supernode reports a
+ * more recent version, and only once. It can be used to trigger a
+ * software update check.</p>
+ *
+ * <p>Meta-data: {@link Version}, more recent version number</p>
+ */
+ EVENT_SAW_MORE_RECENT_VERSION,
+
+ /**
+ * A packet failed authentication
+ *
+ * <p>Meta-data: {@link InetSocketAddress} containing origin address of packet</p>
+ */
+ EVENT_AUTHENTICATION_FAILURE,
+
+ /**
+ * A received packet was not valid
+ *
+ * <p>Meta-data: {@link InetSocketAddress} containing origin address of packet</p>
+ */
+ EVENT_INVALID_PACKET,
+
+ /**
+ * Trace (debugging) message
+ *
+ * <p>These events are only generated if this is a TRACE-enabled build.</p>
+ *
+ * <p>Meta-data: {@link String}, TRACE message</p>
+ */
+ EVENT_TRACE
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/EventListener.java b/java/src/com/zerotier/sdk/EventListener.java
new file mode 100644
index 00000000..078023d9
--- /dev/null
+++ b/java/src/com/zerotier/sdk/EventListener.java
@@ -0,0 +1,67 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+import java.net.InetSocketAddress;
+import java.lang.String;
+
+/**
+ * Interface to handle callbacks for ZeroTier One events.
+ */
+public interface EventListener {
+ /**
+ * Callback for events with no other associated metadata
+ *
+ * @param event {@link Event} enum
+ */
+ public void onEvent(Event event);
+
+ /**
+ * Callback for network error events: {@link Event.EVENT_AUTHENTICATION_FAILUER}, {link Event.EVENT_INVALID_PACKET}
+ *
+ * @param event {@link Event} enum
+ * @param source {@link InetSocketAddress} containing the origin address of the packet
+ */
+ public void onNetworkError(Event event, InetSocketAddress source);
+
+ /**
+ * Callback when the node detects that it's out of date.
+ *
+ * @param newVersion {@link Version} object with the latest version of ZeroTier One
+ */
+ public void onOutOfDate(Version newVersion);
+
+ /**
+ * Trace messages
+ *
+ * <p>These events are only generated if the underlying ZeroTierOne SDK is a TRACE-enabled build.</p>
+ *
+ * @param message the trace message
+ */
+ public void onTrace(String message);
+}
diff --git a/java/src/com/zerotier/sdk/MulticastGroup.java b/java/src/com/zerotier/sdk/MulticastGroup.java
new file mode 100644
index 00000000..5c4df87a
--- /dev/null
+++ b/java/src/com/zerotier/sdk/MulticastGroup.java
@@ -0,0 +1,49 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+package com.zerotier.sdk;
+
+
+public final class MulticastGroup {
+ private MulticastGroup() {}
+
+ private long mac;
+ private long adi;
+
+ /**
+ * MAC address (least significant 48 bits)
+ */
+ public final long getMacAddress() {
+ return mac;
+ }
+
+ /**
+ * Additional distinguishing information (usually zero)
+ */
+ public final long getAdi() {
+ return adi;
+ }
+}
diff --git a/java/src/com/zerotier/sdk/NativeUtils.java b/java/src/com/zerotier/sdk/NativeUtils.java
new file mode 100644
index 00000000..07e1ef5b
--- /dev/null
+++ b/java/src/com/zerotier/sdk/NativeUtils.java
@@ -0,0 +1,93 @@
+package com.zerotier.sdk;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Simple library class for working with JNI (Java Native Interface)
+ *
+ * @see http://adamheinrich.com/2012/how-to-load-native-jni-library-from-jar
+ *
+ * @author Adam Heirnich <adam@adamh.cz>, http://www.adamh.cz
+ */
+public class NativeUtils {
+
+ /**
+ * Private constructor - this class will never be instanced
+ */
+ private NativeUtils() {
+ }
+
+ /**
+ * Loads library from current JAR archive
+ *
+ * The file from JAR is copied into system temporary directory and then loaded. The temporary file is deleted after exiting.
+ * Method uses String as filename because the pathname is "abstract", not system-dependent.
+ *
+ * @param filename The filename inside JAR as absolute path (beginning with '/'), e.g. /package/File.ext
+ * @throws IOException If temporary file creation or read/write operation fails
+ * @throws IllegalArgumentException If source file (param path) does not exist
+ * @throws IllegalArgumentException If the path is not absolute or if the filename is shorter than three characters (restriction of {@see File#createTempFile(java.lang.String, java.lang.String)}).
+ */
+ public static void loadLibraryFromJar(String path) throws IOException {
+
+ if (!path.startsWith("/")) {
+ throw new IllegalArgumentException("The path has to be absolute (start with '/').");
+ }
+
+ // Obtain filename from path
+ String[] parts = path.split("/");
+ String filename = (parts.length > 1) ? parts[parts.length - 1] : null;
+
+ // Split filename to prexif and suffix (extension)
+ String prefix = "";
+ String suffix = null;
+ if (filename != null) {
+ parts = filename.split("\\.", 2);
+ prefix = parts[0];
+ suffix = (parts.length > 1) ? "."+parts[parts.length - 1] : null; // Thanks, davs! :-)
+ }
+
+ // Check if the filename is okay
+ if (filename == null || prefix.length() < 3) {
+ throw new IllegalArgumentException("The filename has to be at least 3 characters long.");
+ }
+
+ // Prepare temporary file
+ File temp = File.createTempFile(prefix, suffix);
+ temp.deleteOnExit();
+
+ if (!temp.exists()) {
+ throw new FileNotFoundException("File " + temp.getAbsolutePath() + " does not exist.");
+ }
+
+ // Prepare buffer for data copying
+ byte[] buffer = new byte[1024];
+ int readBytes;
+
+ // Open and check input stream
+ InputStream is = NativeUtils.class.getResourceAsStream(path);
+ if (is == null) {
+ throw new FileNotFoundException("File " + path + " was not found inside JAR.");
+ }
+
+ // Open output stream and copy data between source file in JAR and the temporary file
+ OutputStream os = new FileOutputStream(temp);
+ try {
+ while ((readBytes = is.read(buffer)) != -1) {
+ os.write(buffer, 0, readBytes);
+ }
+ } finally {
+ // If read/write fails, close streams safely before throwing an exception
+ os.close();
+ is.close();
+ }
+
+ // Finally, load the library
+ System.load(temp.getAbsolutePath());
+ }
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/Node.java b/java/src/com/zerotier/sdk/Node.java
new file mode 100644
index 00000000..34af0d01
--- /dev/null
+++ b/java/src/com/zerotier/sdk/Node.java
@@ -0,0 +1,432 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.io.IOException;
+
+/**
+ * A ZeroTier One node
+ */
+public class Node {
+ static {
+ try {
+ System.loadLibrary("ZeroTierOneJNI");
+ } catch (UnsatisfiedLinkError e) {
+ try {
+ if(System.getProperty("os.name").startsWith("Windows")) {
+ System.out.println("Arch: " + System.getProperty("sun.arch.data.model"));
+ if(System.getProperty("sun.arch.data.model").equals("64")) {
+ NativeUtils.loadLibraryFromJar("/lib/ZeroTierOneJNI_win64.dll");
+ } else {
+ NativeUtils.loadLibraryFromJar("/lib/ZeroTierOneJNI_win32.dll");
+ }
+ } else if(System.getProperty("os.name").startsWith("Mac")) {
+ NativeUtils.loadLibraryFromJar("/lib/libZeroTierOneJNI.jnilib");
+ } else {
+ // TODO: Linux
+ }
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+
+ private static final String TAG = "NODE";
+
+ /**
+ * Node ID for JNI purposes.
+ * Currently set to the now value passed in at the constructor
+ *
+ * -1 if the node has already been closed
+ */
+ private long nodeId;
+
+ private final DataStoreGetListener getListener;
+ private final DataStorePutListener putListener;
+ private final PacketSender sender;
+ private final EventListener eventListener;
+ private final VirtualNetworkFrameListener frameListener;
+ private final VirtualNetworkConfigListener configListener;
+
+ /**
+ * Create a new ZeroTier One node
+ *
+ * <p>Note that this can take a few seconds the first time it's called, as it
+ * will generate an identity.</p>
+ *
+ * @param now Current clock in milliseconds
+ * @param getListener User written instance of the {@link DataStoreGetListener} interface called to get objects from persistent storage. This instance must be unique per Node object.
+ * @param putListener User written intstance of the {@link DataStorePutListener} interface called to put objects in persistent storage. This instance must be unique per Node object.
+ * @param sender
+ * @param eventListener User written instance of the {@link EventListener} interface to receive status updates and non-fatal error notices. This instance must be unique per Node object.
+ * @param frameListener
+ * @param configListener User written instance of the {@link VirtualNetworkConfigListener} interface to be called when virtual LANs are created, deleted, or their config parameters change. This instance must be unique per Node object.
+ */
+ public Node(long now,
+ DataStoreGetListener getListener,
+ DataStorePutListener putListener,
+ PacketSender sender,
+ EventListener eventListener,
+ VirtualNetworkFrameListener frameListener,
+ VirtualNetworkConfigListener configListener) throws NodeException
+ {
+ this.nodeId = now;
+
+ this.getListener = getListener;
+ this.putListener = putListener;
+ this.sender = sender;
+ this.eventListener = eventListener;
+ this.frameListener = frameListener;
+ this.configListener = configListener;
+
+ ResultCode rc = node_init(now);
+ if(rc != ResultCode.RESULT_OK)
+ {
+ // TODO: Throw Exception
+ throw new NodeException(rc.toString());
+ }
+ }
+
+ /**
+ * Close this Node.
+ *
+ * <p>The Node object can no longer be used once this method is called.</p>
+ */
+ public void close() {
+ if(nodeId != -1) {
+ node_delete(nodeId);
+ nodeId = -1;
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ close();
+ }
+
+ /**
+ * Process a frame from a virtual network port
+ *
+ * @param now Current clock in milliseconds
+ * @param nwid ZeroTier 64-bit virtual network ID
+ * @param sourceMac Source MAC address (least significant 48 bits)
+ * @param destMac Destination MAC address (least significant 48 bits)
+ * @param etherType 16-bit Ethernet frame type
+ * @param vlanId 10-bit VLAN ID or 0 if none
+ * @param frameData Frame payload data
+ * @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
+ * @return OK (0) or error code if a fatal error condition has occurred
+ */
+ public ResultCode processVirtualNetworkFrame(
+ long now,
+ long nwid,
+ long sourceMac,
+ long destMac,
+ int etherType,
+ int vlanId,
+ byte[] frameData,
+ long[] nextBackgroundTaskDeadline) {
+ return processVirtualNetworkFrame(
+ nodeId, now, nwid, sourceMac, destMac, etherType, vlanId,
+ frameData, nextBackgroundTaskDeadline);
+ }
+
+ /**
+ * Process a packet received from the physical wire
+ *
+ * @param now Current clock in milliseconds
+ * @param remoteAddress Origin of packet
+ * @param packetData Packet data
+ * @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
+ * @return OK (0) or error code if a fatal error condition has occurred
+ */
+ public ResultCode processWirePacket(
+ long now,
+ InetSocketAddress remoteAddress,
+ byte[] packetData,
+ long[] nextBackgroundTaskDeadline) {
+ return processWirePacket(
+ nodeId, now, remoteAddress, packetData,
+ nextBackgroundTaskDeadline);
+ }
+
+ /**
+ * Perform periodic background operations
+ *
+ * @param now Current clock in milliseconds
+ * @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
+ * @return OK (0) or error code if a fatal error condition has occurred
+ */
+ public ResultCode processBackgroundTasks(long now, long[] nextBackgroundTaskDeadline) {
+ return processBackgroundTasks(nodeId, now, nextBackgroundTaskDeadline);
+ }
+
+ /**
+ * Join a network
+ *
+ * <p>This may generate calls to the port config callback before it returns,
+ * or these may be deffered if a netconf is not available yet.</p>
+ *
+ * <p>If we are already a member of the network, nothing is done and OK is
+ * returned.</p>
+ *
+ * @param nwid 64-bit ZeroTier network ID
+ * @return OK (0) or error code if a fatal error condition has occurred
+ */
+ public ResultCode join(long nwid) {
+ return join(nodeId, nwid);
+ }
+
+ /**
+ * Leave a network
+ *
+ * <p>If a port has been configured for this network this will generate a call
+ * to the port config callback with a NULL second parameter to indicate that
+ * the port is now deleted.</p>
+ *
+ * @param nwid 64-bit network ID
+ * @return OK (0) or error code if a fatal error condition has occurred
+ */
+ public ResultCode leave(long nwid) {
+ return leave(nodeId, nwid);
+ }
+
+ /**
+ * Subscribe to an Ethernet multicast group
+ *
+ * <p>For IPv4 ARP, the implementation must subscribe to 0xffffffffffff (the
+ * broadcast address) but with an ADI equal to each IPv4 address in host
+ * byte order. This converts ARP from a non-scalable broadcast protocol to
+ * a scalable multicast protocol with perfect address specificity.</p>
+ *
+ * <p>If this is not done, ARP will not work reliably.</p>
+ *
+ * <p>Multiple calls to subscribe to the same multicast address will have no
+ * effect. It is perfectly safe to do this.</p>
+ *
+ * <p>This does not generate an update call to the {@link VirtualNetworkConfigListener#onNetworkConfigurationUpdated} method.</p>
+ *
+ * @param nwid 64-bit network ID
+ * @param multicastGroup Ethernet multicast or broadcast MAC (least significant 48 bits)
+ * @return OK (0) or error code if a fatal error condition has occurred
+ */
+ public ResultCode multicastSubscribe(
+ long nwid,
+ long multicastGroup) {
+ return multicastSubscribe(nodeId, nwid, multicastGroup, 0);
+ }
+
+ /**
+ * Subscribe to an Ethernet multicast group
+ *
+ * <p>ADI stands for additional distinguishing information. This defaults to zero
+ * and is rarely used. Right now its only use is to enable IPv4 ARP to scale,
+ * and this must be done.</p>
+ *
+ * <p>For IPv4 ARP, the implementation must subscribe to 0xffffffffffff (the
+ * broadcast address) but with an ADI equal to each IPv4 address in host
+ * byte order. This converts ARP from a non-scalable broadcast protocol to
+ * a scalable multicast protocol with perfect address specificity.</p>
+ *
+ * <p>If this is not done, ARP will not work reliably.</p>
+ *
+ * <p>Multiple calls to subscribe to the same multicast address will have no
+ * effect. It is perfectly safe to do this.</p>
+ *
+ * <p>This does not generate an update call to the {@link VirtualNetworkConfigListener#onNetworkConfigurationUpdated} method.</p>
+ *
+ * @param nwid 64-bit network ID
+ * @param multicastGroup Ethernet multicast or broadcast MAC (least significant 48 bits)
+ * @param multicastAdi Multicast ADI (least significant 32 bits only, default: 0)
+ * @return OK (0) or error code if a fatal error condition has occurred
+ */
+ public ResultCode multicastSubscribe(
+ long nwid,
+ long multicastGroup,
+ long multicastAdi) {
+ return multicastSubscribe(nodeId, nwid, multicastGroup, multicastAdi);
+ }
+
+
+ /**
+ * Unsubscribe from an Ethernet multicast group (or all groups)
+ *
+ * <p>If multicastGroup is zero (0), this will unsubscribe from all groups. If
+ * you are not subscribed to a group this has no effect.</p>
+ *
+ * <p>This does not generate an update call to the {@link VirtualNetworkConfigListener#onNetworkConfigurationUpdated} method.</p>
+ *
+ * @param nwid 64-bit network ID
+ * @param multicastGroup Ethernet multicast or broadcast MAC (least significant 48 bits)
+ * @return OK (0) or error code if a fatal error condition has occurred
+ */
+ public ResultCode multicastUnsubscribe(
+ long nwid,
+ long multicastGroup) {
+ return multicastUnsubscribe(nodeId, nwid, multicastGroup, 0);
+ }
+
+ /**
+ * Unsubscribe from an Ethernet multicast group (or all groups)
+ *
+ * <p>If multicastGroup is zero (0), this will unsubscribe from all groups. If
+ * you are not subscribed to a group this has no effect.</p>
+ *
+ * <p>This does not generate an update call to the {@link VirtualNetworkConfigListener#onNetworkConfigurationUpdated} method.</p>
+ *
+ * <p>ADI stands for additional distinguishing information. This defaults to zero
+ * and is rarely used. Right now its only use is to enable IPv4 ARP to scale,
+ * and this must be done.</p>
+ *
+ * @param nwid 64-bit network ID
+ * @param multicastGroup Ethernet multicast or broadcast MAC (least significant 48 bits)
+ * @param multicastAdi Multicast ADI (least significant 32 bits only, default: 0)
+ * @return OK (0) or error code if a fatal error condition has occurred
+ */
+ public ResultCode multicastUnsubscribe(
+ long nwid,
+ long multicastGroup,
+ long multicastAdi) {
+ return multicastUnsubscribe(nodeId, nwid, multicastGroup, multicastAdi);
+ }
+
+ /**
+ * Get this node's 40-bit ZeroTier address
+ *
+ * @return ZeroTier address (least significant 40 bits of 64-bit int)
+ */
+ public long address() {
+ return address(nodeId);
+ }
+
+ /**
+ * Get the status of this node
+ *
+ * @return @{link NodeStatus} struct with the current node status.
+ */
+ public NodeStatus status() {
+ return status(nodeId);
+ }
+
+ /**
+ * Get a list of known peer nodes
+ *
+ * @return List of known peers or NULL on failure
+ */
+ public ArrayList<Peer> peers() {
+ return peers(nodeId);
+ }
+
+ /**
+ * Get the status of a virtual network
+ *
+ * @param nwid 64-bit network ID
+ * @return {@link VirtualNetworkConfig} or NULL if we are not a member of this network
+ */
+ public VirtualNetworkConfig networkConfig(long nwid) {
+ return networkConfig(nodeId, nwid);
+ }
+
+ /**
+ * Enumerate and get status of all networks
+ *
+ * @return List of networks or NULL on failure
+ */
+ public ArrayList<VirtualNetworkConfig> networks() {
+ return networks(nodeId);
+ }
+
+ /**
+ * Get ZeroTier One version
+ *
+ * @return {@link Version} object with ZeroTierOne version information.
+ */
+ public Version getVersion() {
+ return version();
+ }
+
+ //
+ // function declarations for JNI
+ //
+ private native ResultCode node_init(long now);
+
+ private native void node_delete(long nodeId);
+
+ private native ResultCode processVirtualNetworkFrame(
+ long nodeId,
+ long now,
+ long nwid,
+ long sourceMac,
+ long destMac,
+ int etherType,
+ int vlanId,
+ byte[] frameData,
+ long[] nextBackgroundTaskDeadline);
+
+ private native ResultCode processWirePacket(
+ long nodeId,
+ long now,
+ InetSocketAddress remoteAddress,
+ byte[] packetData,
+ long[] nextBackgroundTaskDeadline);
+
+ private native ResultCode processBackgroundTasks(
+ long nodeId,
+ long now,
+ long[] nextBackgroundTaskDeadline);
+
+ private native ResultCode join(long nodeId, long nwid);
+
+ private native ResultCode leave(long nodeId, long nwid);
+
+ private native ResultCode multicastSubscribe(
+ long nodeId,
+ long nwid,
+ long multicastGroup,
+ long multicastAdi);
+
+ private native ResultCode multicastUnsubscribe(
+ long nodeId,
+ long nwid,
+ long multicastGroup,
+ long multicastAdi);
+
+ private native long address(long nodeId);
+
+ private native NodeStatus status(long nodeId);
+
+ private native VirtualNetworkConfig networkConfig(long nodeId, long nwid);
+
+ private native Version version();
+
+ private native ArrayList<Peer> peers(long nodeId);
+
+ private native ArrayList<VirtualNetworkConfig> networks(long nodeId);
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/NodeException.java b/java/src/com/zerotier/sdk/NodeException.java
new file mode 100644
index 00000000..3071069a
--- /dev/null
+++ b/java/src/com/zerotier/sdk/NodeException.java
@@ -0,0 +1,36 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+import java.lang.RuntimeException;
+
+class NodeException extends RuntimeException {
+ NodeException(String message) {
+ super(message);
+ }
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/NodeStatus.java b/java/src/com/zerotier/sdk/NodeStatus.java
new file mode 100644
index 00000000..94376d85
--- /dev/null
+++ b/java/src/com/zerotier/sdk/NodeStatus.java
@@ -0,0 +1,69 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+public final class NodeStatus {
+ private long address;
+ private String publicIdentity;
+ private String secretIdentity;
+ private boolean online;
+
+ private NodeStatus() {}
+
+ /**
+ * 40-bit ZeroTier address of this node
+ */
+ public final long getAddres() {
+ return address;
+ }
+
+ /**
+ * Public identity in string-serialized form (safe to send to others)
+ *
+ * <p>This identity will remain valid as long as the node exists.</p>
+ */
+ public final String getPublicIdentity() {
+ return publicIdentity;
+ }
+
+ /**
+ * Full identity including secret key in string-serialized form
+ *
+ * <p>This identity will remain valid as long as the node exists.</p>
+ */
+ public final String getSecretIdentity() {
+ return secretIdentity;
+ }
+
+ /**
+ * True if some kind of connectivity appears available
+ */
+ public final boolean isOnline() {
+ return online;
+ }
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/PacketSender.java b/java/src/com/zerotier/sdk/PacketSender.java
new file mode 100644
index 00000000..5302f5ce
--- /dev/null
+++ b/java/src/com/zerotier/sdk/PacketSender.java
@@ -0,0 +1,47 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+package com.zerotier.sdk;
+
+import java.net.InetSocketAddress;
+
+
+public interface PacketSender {
+ /**
+ * Function to send a ZeroTier packet out over the wire
+ *
+ * <p>The function must return zero on success and may return any error code
+ * on failure. Note that success does not (of course) guarantee packet
+ * delivery. It only means that the packet appears to have been sent.</p>
+ *
+ * @param addr {@link InetSocketAddress} to send to
+ * @param packetData data to send
+ * @return 0 on success, any error code on failure.
+ */
+ public int onSendPacketRequested(
+ InetSocketAddress addr,
+ byte[] packetData);
+}
diff --git a/java/src/com/zerotier/sdk/Peer.java b/java/src/com/zerotier/sdk/Peer.java
new file mode 100644
index 00000000..407e6cd4
--- /dev/null
+++ b/java/src/com/zerotier/sdk/Peer.java
@@ -0,0 +1,110 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+import java.util.ArrayList;
+
+/**
+ * Peer status result
+ */
+public final class Peer {
+ private long address;
+ private long lastUnicastFrame;
+ private long lastMulticastFrame;
+ private int versionMajor;
+ private int versionMinor;
+ private int versionRev;
+ private int latency;
+ private PeerRole role;
+ private ArrayList<PeerPhysicalPath> paths;
+
+ private Peer() {}
+
+ /**
+ * ZeroTier address (40 bits)
+ */
+ public final long address() {
+ return address;
+ }
+
+ /**
+ * Time we last received a unicast frame from this peer
+ */
+ public final long lastUnicastFrame() {
+ return lastUnicastFrame;
+ }
+
+ /**
+ * Time we last received a multicast rame from this peer
+ */
+ public final long lastMulticastFrame() {
+ return lastMulticastFrame;
+ }
+
+ /**
+ * Remote major version or -1 if not known
+ */
+ public final int versionMajor() {
+ return versionMajor;
+ }
+
+ /**
+ * Remote minor version or -1 if not known
+ */
+ public final int versionMinor() {
+ return versionMinor;
+ }
+
+ /**
+ * Remote revision or -1 if not known
+ */
+ public final int versionRev() {
+ return versionRev;
+ }
+
+ /**
+ * Last measured latency in milliseconds or zero if unknown
+ */
+ public final int latency() {
+ return latency;
+ }
+
+ /**
+ * What trust hierarchy role does this device have?
+ */
+ public final PeerRole role() {
+ return role;
+ }
+
+ /**
+ * Known network paths to peer
+ */
+ public final ArrayList<PeerPhysicalPath> paths() {
+ return paths;
+ }
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/PeerPhysicalPath.java b/java/src/com/zerotier/sdk/PeerPhysicalPath.java
new file mode 100644
index 00000000..d64ea56b
--- /dev/null
+++ b/java/src/com/zerotier/sdk/PeerPhysicalPath.java
@@ -0,0 +1,86 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+import java.net.InetSocketAddress;
+
+/**
+ * Physical network path to a peer
+ */
+public final class PeerPhysicalPath {
+ private InetSocketAddress address;
+ private long lastSend;
+ private long lastReceive;
+ private boolean fixed;
+ private boolean active;
+ private boolean preferred;
+
+ private PeerPhysicalPath() {}
+
+ /**
+ * Address of endpoint
+ */
+ public final InetSocketAddress address() {
+ return address;
+ }
+
+ /**
+ * Time of last send in milliseconds or 0 for never
+ */
+ public final long lastSend() {
+ return lastSend;
+ }
+
+ /**
+ * Time of last receive in milliseconds or 0 for never
+ */
+ public final long lastReceive() {
+ return lastReceive;
+ }
+
+ /**
+ * Is path fixed? (i.e. not learned, static)
+ */
+ public final boolean isFixed() {
+ return fixed;
+ }
+
+ /**
+ * Is path active?
+ */
+ public final boolean isActive() {
+ return active;
+ }
+
+ /**
+ * Is path preferred?
+ */
+ public final boolean isPreferred() {
+ return preferred;
+ }
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/PeerRole.java b/java/src/com/zerotier/sdk/PeerRole.java
new file mode 100644
index 00000000..56c3ac0c
--- /dev/null
+++ b/java/src/com/zerotier/sdk/PeerRole.java
@@ -0,0 +1,45 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+public enum PeerRole {
+ /**
+ * An ordinary node
+ */
+ PEER_ROLE_LEAF,
+
+ /**
+ * Locally federated hub
+ */
+ PEER_ROLE_HUB,
+
+ /**
+ * planetary supernode
+ */
+ PEER_ROLE_SUPERNODE
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/ResultCode.java b/java/src/com/zerotier/sdk/ResultCode.java
new file mode 100644
index 00000000..5da82b31
--- /dev/null
+++ b/java/src/com/zerotier/sdk/ResultCode.java
@@ -0,0 +1,74 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+/**
+ * Function return code: OK (0) or error results
+ *
+ * <p>Use {@link ResultCode#isFatal) to check for a fatal error. If a fatal error
+ * occurs, the node should be considered to not be working correctly. These
+ * indicate serious problems like an inaccessible data store or a compile
+ * problem.</p>
+ */
+public enum ResultCode {
+ /**
+ * Operation completed normally
+ */
+ RESULT_OK(0),
+
+ // Fatal errors (> 0, < 1000)
+ /**
+ * Ran out of memory
+ */
+ RESULT_FATAL_ERROR_OUT_OF_MEMORY(1),
+
+ /**
+ * Data store is not writable or has failed
+ */
+ RESULT_FATAL_ERROR_DATA_STORE_FAILED(2),
+
+ /**
+ * Internal error (e.g. unexpected exception indicating bug or build problem)
+ */
+ RESULT_FATAL_ERROR_INTERNAL(3),
+
+ // non-fatal errors
+
+ /**
+ * Network ID not valid
+ */
+ RESULT_ERROR_NETWORK_NOT_FOUND(1000);
+
+ private final int id;
+ ResultCode(int id) { this.id = id; }
+ public int getValue() { return id; }
+
+ public boolean isFatal(int id) {
+ return (id > 0 && id < 1000);
+ }
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/Version.java b/java/src/com/zerotier/sdk/Version.java
new file mode 100644
index 00000000..d7fa0ce4
--- /dev/null
+++ b/java/src/com/zerotier/sdk/Version.java
@@ -0,0 +1,37 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+public final class Version {
+ private Version() {}
+
+ public int major = 0;
+ public int minor = 0;
+ public int revision = 0;
+ public long featureFlags = 0;
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java
new file mode 100644
index 00000000..032ba267
--- /dev/null
+++ b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java
@@ -0,0 +1,170 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+import java.lang.String;
+import java.util.ArrayList;
+import java.net.InetSocketAddress;
+
+public final class VirtualNetworkConfig {
+ public static final int MAX_MULTICAST_SUBSCRIPTIONS = 4096;
+ public static final int ZT1_MAX_ZT_ASSIGNED_ADDRESSES = 16;
+
+ private long nwid;
+ private long mac;
+ private String name;
+ private VirtualNetworkStatus status;
+ private VirtualNetworkType type;
+ private int mtu;
+ private boolean dhcp;
+ private boolean bridge;
+ private boolean broadcastEnabled;
+ private int portError;
+ private boolean enabled;
+ private long netconfRevision;
+ private ArrayList<MulticastGroup> multicastSubscriptions;
+ private ArrayList<InetSocketAddress> assignedAddresses;
+
+ private VirtualNetworkConfig() {
+
+ }
+
+ /**
+ * 64-bit ZeroTier network ID
+ */
+ public final long networkId() {
+ return nwid;
+ }
+
+ /**
+ * Ethernet MAC (40 bits) that should be assigned to port
+ */
+ public final long macAddress() {
+ return mac;
+ }
+
+ /**
+ * Network name (from network configuration master)
+ */
+ public final String name() {
+ return name;
+ }
+
+ /**
+ * Network configuration request status
+ */
+ public final VirtualNetworkStatus networkStatus() {
+ return status;
+ }
+
+ /**
+ * Network type
+ */
+ public final VirtualNetworkType networkType() {
+ return type;
+ }
+
+ /**
+ * Maximum interface MTU
+ */
+ public final int mtu() {
+ return mtu;
+ }
+
+ /**
+ * If the network this port belongs to indicates DHCP availability
+ *
+ * <p>This is a suggestion. The underlying implementation is free to ignore it
+ * for security or other reasons. This is simply a netconf parameter that
+ * means 'DHCP is available on this network.'</p>
+ */
+ public final boolean isDhcpAvailable() {
+ return dhcp;
+ }
+
+ /**
+ * If this port is allowed to bridge to other networks
+ *
+ * <p>This is informational. If this is false, bridged packets will simply
+ * be dropped and bridging won't work.</p>
+ */
+ public final boolean isBridgeEnabled() {
+ return bridge;
+ }
+
+ /**
+ * If true, this network supports and allows broadcast (ff:ff:ff:ff:ff:ff) traffic
+ */
+ public final boolean broadcastEnabled() {
+ return broadcastEnabled;
+ }
+
+ /**
+ * If the network is in PORT_ERROR state, this is the error most recently returned by the port config callback
+ */
+ public final int portError() {
+ return portError;
+ }
+
+ /**
+ * Is this network enabled? If not, all frames to/from are dropped.
+ */
+ public final boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Network config revision as reported by netconf master
+ *
+ * <p>If this is zero, it means we're still waiting for our netconf.</p>
+ */
+ public final long netconfRevision() {
+ return netconfRevision;
+ }
+
+ /**
+ * Multicast group subscriptions
+ */
+ public final ArrayList<MulticastGroup> multicastSubscriptions() {
+ return multicastSubscriptions;
+ }
+
+ /**
+ * ZeroTier-assigned addresses (in {@link java.net.InetSocketAddress} objects)
+ *
+ * For IP, the port number of the sockaddr_XX structure contains the number
+ * of bits in the address netmask. Only the IP address and port are used.
+ * Other fields like interface number can be ignored.
+ *
+ * This is only used for ZeroTier-managed address assignments sent by the
+ * virtual network's configuration master.
+ */
+ public final ArrayList<InetSocketAddress> assignedAddresses() {
+ return assignedAddresses;
+ }
+}
diff --git a/java/src/com/zerotier/sdk/VirtualNetworkConfigListener.java b/java/src/com/zerotier/sdk/VirtualNetworkConfigListener.java
new file mode 100644
index 00000000..15ae301c
--- /dev/null
+++ b/java/src/com/zerotier/sdk/VirtualNetworkConfigListener.java
@@ -0,0 +1,60 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+
+package com.zerotier.sdk;
+
+
+public interface VirtualNetworkConfigListener {
+ /**
+ * Callback called to update virtual network port configuration
+ *
+ * <p>This can be called at any time to update the configuration of a virtual
+ * network port. The parameter after the network ID specifies whether this
+ * port is being brought up, updated, brought down, or permanently deleted.
+ *
+ * This in turn should be used by the underlying implementation to create
+ * and configure tap devices at the OS (or virtual network stack) layer.</P>
+ *
+ * This should not call {@link Node#multicastSubscribe} or other network-modifying
+ * methods, as this could cause a deadlock in multithreaded or interrupt
+ * driven environments.
+ *
+ * This must return 0 on success. It can return any OS-dependent error code
+ * on failure, and this results in the network being placed into the
+ * PORT_ERROR state.
+ *
+ * @param nwid network id
+ * @param op {@link VirtualNetworkConfigOperation} enum describing the configuration operation
+ * @param config {@link VirtualNetworkConfig} object with the new configuration
+ * @return 0 on success
+ */
+ public int onNetworkConfigurationUpdated(
+ long nwid,
+ VirtualNetworkConfigOperation op,
+ VirtualNetworkConfig config);
+} \ No newline at end of file
diff --git a/java/src/com/zerotier/sdk/VirtualNetworkConfigOperation.java b/java/src/com/zerotier/sdk/VirtualNetworkConfigOperation.java
new file mode 100644
index 00000000..b70eb478
--- /dev/null
+++ b/java/src/com/zerotier/sdk/VirtualNetworkConfigOperation.java
@@ -0,0 +1,49 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+package com.zerotier.sdk;
+
+public enum VirtualNetworkConfigOperation {
+ /**
+ * Network is coming up (either for the first time or after service restart)
+ */
+ VIRTUAL_NETWORK_CONFIG_OPERATION_UP,
+
+ /**
+ * Network configuration has been updated
+ */
+ VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE,
+
+ /**
+ * Network is going down (not permanently)
+ */
+ VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,
+
+ /**
+ * Network is going down permanently (leave/delete)
+ */
+ VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY
+}
diff --git a/java/src/com/zerotier/sdk/VirtualNetworkFrameListener.java b/java/src/com/zerotier/sdk/VirtualNetworkFrameListener.java
new file mode 100644
index 00000000..9ad32282
--- /dev/null
+++ b/java/src/com/zerotier/sdk/VirtualNetworkFrameListener.java
@@ -0,0 +1,48 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+package com.zerotier.sdk;
+
+public interface VirtualNetworkFrameListener {
+ /**
+ * Function to send a frame out to a virtual network port
+ *
+ * @param nwid ZeroTier One network ID
+ * @param srcMac source MAC address
+ * @param destMac destination MAC address
+ * @param ethertype
+ * @param vlanId
+ * @param frameData data to send
+ */
+ public void onVirtualNetworkFrame(
+ long nwid,
+ long srcMac,
+ long destMac,
+ long etherType,
+ long vlanId,
+ byte[] frameData);
+}
diff --git a/java/src/com/zerotier/sdk/VirtualNetworkStatus.java b/java/src/com/zerotier/sdk/VirtualNetworkStatus.java
new file mode 100644
index 00000000..2d00561a
--- /dev/null
+++ b/java/src/com/zerotier/sdk/VirtualNetworkStatus.java
@@ -0,0 +1,59 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+package com.zerotier.sdk;
+
+public enum VirtualNetworkStatus {
+ /**
+ * Waiting for network configuration (also means revision == 0)
+ */
+ NETWORK_STATUS_REQUESTING_CONFIGURATION,
+
+ /**
+ * Configuration received and we are authorized
+ */
+ NETWORK_STATUS_OK,
+
+ /**
+ * Netconf master told us 'nope'
+ */
+ NETWORK_STATUS_ACCESS_DENIED,
+
+ /**
+ * Netconf master exists, but this virtual network does not
+ */
+ NETWORK_STATUS_NOT_FOUND,
+
+ /**
+ * Initialization of network failed or other internal error
+ */
+ NETWORK_STATUS_PORT_ERROR,
+
+ /**
+ * ZeroTier One version too old
+ */
+ NETWORK_STATUS_CLIENT_TOO_OLD
+}
diff --git a/java/src/com/zerotier/sdk/VirtualNetworkType.java b/java/src/com/zerotier/sdk/VirtualNetworkType.java
new file mode 100644
index 00000000..ab1f4e08
--- /dev/null
+++ b/java/src/com/zerotier/sdk/VirtualNetworkType.java
@@ -0,0 +1,39 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+package com.zerotier.sdk;
+
+public enum VirtualNetworkType {
+ /**
+ * Private networks are authorized via certificates of membership
+ */
+ NETWORK_TYPE_PRIVATE,
+
+ /**
+ * Public networks have no access control -- they'll always be AUTHORIZED
+ */
+ NETWORK_TYPE_PUBLIC
+}