diff --git a/java/jni/ZT_jniarray.cpp b/java/jni/ZT_jniarray.cpp deleted file mode 100644 index a1cae76ed..000000000 --- a/java/jni/ZT_jniarray.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// -// Created by Grant Limberg on 10/21/20. -// - -#include "ZT_jniarray.h" -#include -#include -#include - -jclass java_util_ArrayList; -jmethodID java_util_ArrayList_; -jmethodID java_util_ArrayList_size; -jmethodID java_util_ArrayList_get; -jmethodID java_util_ArrayList_add; - -void InitListJNI(JNIEnv* env) { - java_util_ArrayList = static_cast(env->NewGlobalRef(env->FindClass("java/util/ArrayList"))); - java_util_ArrayList_ = env->GetMethodID(java_util_ArrayList, "", "(I)V"); - java_util_ArrayList_size = env->GetMethodID (java_util_ArrayList, "size", "()I"); - java_util_ArrayList_get = env->GetMethodID(java_util_ArrayList, "get", "(I)Ljava/lang/Object;"); - java_util_ArrayList_add = env->GetMethodID(java_util_ArrayList, "add", "(Ljava/lang/Object;)Z"); -} - -jclass ListJNI::getListClass(JNIEnv* env) { - jclass jclazz = env->FindClass("java/util/List"); - assert(jclazz != nullptr); - return jclazz; -} - -jclass ListJNI::getArrayListClass(JNIEnv* env) { - jclass jclazz = env->FindClass("java/util/ArrayList"); - assert(jclazz != nullptr); - return jclazz; -} - -jclass ListJNI::getIteratorClass(JNIEnv* env) { - jclass jclazz = env->FindClass("java/util/Iterator"); - assert(jclazz != nullptr); - return jclazz; -} - -jmethodID ListJNI::getIteratorMethod(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getListClass(env), "iterator", "()Ljava/util/Iterator;"); - assert(mid != nullptr); - return mid; -} - -jmethodID ListJNI::getHasNextMethod(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getIteratorClass(env), "hasNext", "()Z"); - assert(mid != nullptr); - return mid; -} - -jmethodID ListJNI::getNextMethod(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getIteratorClass(env), "next", "()Ljava/lang/Object;"); - assert(mid != nullptr); - return mid; -} - -jmethodID ListJNI::getArrayListConstructorMethodId(JNIEnv* env, jclass jclazz) { - static jmethodID mid = env->GetMethodID( - jclazz, "", "(I)V"); - assert(mid != nullptr); - return mid; -} - -jmethodID ListJNI::getListAddMethodId(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getListClass(env), "add", "(Ljava/lang/Object;)Z"); - assert(mid != nullptr); - return mid; -} - -jclass ByteJNI::getByteClass(JNIEnv* env) { - jclass jclazz = env->FindClass("java/lang/Byte"); - assert(jclazz != nullptr); - return jclazz; -} - -jmethodID ByteJNI::getByteValueMethod(JNIEnv* env) { - static jmethodID mid = env->GetMethodID( - getByteClass(env), "byteValue", "()B"); - assert(mid != nullptr); - return mid; -} - -jobject cppToJava(JNIEnv* env, std::vector vector) { - jobject result = env->NewObject(java_util_ArrayList, java_util_ArrayList_, vector.size()); - for (std::string s: vector) { - jstring element = env->NewStringUTF(s.c_str()); - env->CallBooleanMethod(result, java_util_ArrayList_add, element); - env->DeleteLocalRef(element); - } - return result; -} - -std::vector javaToCpp(JNIEnv* env, jobject arrayList) { - jint len = env->CallIntMethod(arrayList, java_util_ArrayList_size); - std::vector result; - result.reserve(len); - for (jint i=0; i(env->CallObjectMethod(arrayList, java_util_ArrayList_get, i)); - const char* pchars = env->GetStringUTFChars(element, nullptr); - result.emplace_back(pchars); - env->ReleaseStringUTFChars(element, pchars); - env->DeleteLocalRef(element); - } - return result; -} diff --git a/java/jni/ZT_jniarray.h b/java/jni/ZT_jniarray.h deleted file mode 100644 index d93c87b9c..000000000 --- a/java/jni/ZT_jniarray.h +++ /dev/null @@ -1,60 +0,0 @@ -// -// Created by Grant Limberg on 10/21/20. -// - -#ifndef ZEROTIERANDROID_ZT_JNIARRAY_H -#define ZEROTIERANDROID_ZT_JNIARRAY_H - -#include -#include -#include - -extern jclass java_util_ArrayList; -extern jmethodID java_util_ArrayList_; -extern jmethodID java_util_ArrayList_size; -extern jmethodID java_util_ArrayList_get; -extern jmethodID java_util_ArrayList_add; - -void InitListJNI(JNIEnv* env); - -class ListJNI { -public: - // Get the java class id of java.util.List. - static jclass getListClass(JNIEnv* env); - - // Get the java class id of java.util.ArrayList. - static jclass getArrayListClass(JNIEnv* env); - - // Get the java class id of java.util.Iterator. - static jclass getIteratorClass(JNIEnv* env); - - // Get the java method id of java.util.List.iterator(). - static jmethodID getIteratorMethod(JNIEnv* env); - - // Get the java method id of java.util.Iterator.hasNext(). - static jmethodID getHasNextMethod(JNIEnv* env); - - // Get the java method id of java.util.Iterator.next(). - static jmethodID getNextMethod(JNIEnv* env); - - // Get the java method id of arrayList constructor. - static jmethodID getArrayListConstructorMethodId(JNIEnv* env, jclass jclazz); - - // Get the java method id of java.util.List.add(). - static jmethodID getListAddMethodId(JNIEnv* env); -}; - -class ByteJNI { -public: - // Get the java class id of java.lang.Byte. - static jclass getByteClass(JNIEnv* env); - - // Get the java method id of java.lang.Byte.byteValue. - static jmethodID getByteValueMethod(JNIEnv* env); -}; - -jobject cppToJava(JNIEnv* env, std::vector vector); - -std::vector javaToCpp(JNIEnv* env, jobject arrayList); - -#endif //ZEROTIERANDROID_ZT_JNIARRAY_H diff --git a/java/jni/ZT_jnicache.cpp b/java/jni/ZT_jnicache.cpp new file mode 100644 index 000000000..c721a9ee1 --- /dev/null +++ b/java/jni/ZT_jnicache.cpp @@ -0,0 +1,236 @@ +// +// Created by Brenton Bostick on 1/18/23. +// + +#include "ZT_jnicache.h" + +#include "ZT_jniutils.h" + +#include + +#define LOG_TAG "Cache" + +#define EXCEPTIONANDNULLCHECK(var) \ + do { \ + if (env->ExceptionCheck()) { \ + assert(false && "Exception"); \ + } \ + if ((var) == NULL) { \ + assert(false && #var " is NULL"); \ + } \ + } while (false) + +#define SETCLASS(classVar, classNameString) \ + do { \ + jclass classVar ## _local = env->FindClass(classNameString); \ + EXCEPTIONANDNULLCHECK(classVar ## _local); \ + classVar = reinterpret_cast(env->NewGlobalRef(classVar ## _local)); \ + EXCEPTIONANDNULLCHECK(classVar); \ + env->DeleteLocalRef(classVar ## _local); \ + } while (false) + +#define SETOBJECT(objectVar, code) \ + do { \ + jobject objectVar ## _local = code; \ + EXCEPTIONANDNULLCHECK(objectVar ## _local); \ + objectVar = env->NewGlobalRef(objectVar ## _local); \ + EXCEPTIONANDNULLCHECK(objectVar); \ + env->DeleteLocalRef(objectVar ## _local); \ + } while (false) + + +// +// Classes +// + +jclass ArrayList_class; +jclass DataStoreGetListener_class; +jclass DataStorePutListener_class; +jclass EventListener_class; +jclass Event_class; +jclass Inet4Address_class; +jclass Inet6Address_class; +jclass InetAddress_class; +jclass InetSocketAddress_class; +jclass NodeStatus_class; +jclass Node_class; +jclass PacketSender_class; +jclass PathChecker_class; +jclass PeerPhysicalPath_class; +jclass PeerRole_class; +jclass Peer_class; +jclass ResultCode_class; +jclass Version_class; +jclass VirtualNetworkConfigListener_class; +jclass VirtualNetworkConfigOperation_class; +jclass VirtualNetworkConfig_class; +jclass VirtualNetworkDNS_class; +jclass VirtualNetworkFrameListener_class; +jclass VirtualNetworkRoute_class; +jclass VirtualNetworkStatus_class; +jclass VirtualNetworkType_class; + +// +// Instance methods +// + +jmethodID ArrayList_add_method; +jmethodID ArrayList_ctor; +jmethodID DataStoreGetListener_onDataStoreGet_method; +jmethodID DataStorePutListener_onDataStorePut_method; +jmethodID DataStorePutListener_onDelete_method; +jmethodID EventListener_onEvent_method; +jmethodID EventListener_onTrace_method; +jmethodID InetAddress_getAddress_method; +jmethodID InetSocketAddress_ctor; +jmethodID InetSocketAddress_getAddress_method; +jmethodID InetSocketAddress_getPort_method; +jmethodID NodeStatus_ctor; +jmethodID PacketSender_onSendPacketRequested_method; +jmethodID PathChecker_onPathCheck_method; +jmethodID PathChecker_onPathLookup_method; +jmethodID PeerPhysicalPath_ctor; +jmethodID Peer_ctor; +jmethodID Version_ctor; +jmethodID VirtualNetworkConfigListener_onNetworkConfigurationUpdated_method; +jmethodID VirtualNetworkConfig_ctor; +jmethodID VirtualNetworkDNS_ctor; +jmethodID VirtualNetworkFrameListener_onVirtualNetworkFrame_method; +jmethodID VirtualNetworkRoute_ctor; + +// +// Static methods +// + +jmethodID Event_fromInt_method; +jmethodID InetAddress_getByAddress_method; +jmethodID PeerRole_fromInt_method; +jmethodID ResultCode_fromInt_method; +jmethodID VirtualNetworkConfigOperation_fromInt_method; +jmethodID VirtualNetworkStatus_fromInt_method; +jmethodID VirtualNetworkType_fromInt_method; + +// +// Enums +// + +jobject ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; +jobject ResultCode_RESULT_OK_enum; + +void setupJNICache(JavaVM *vm) { + + JNIEnv *env; + GETENV(env, vm); + + // + // Classes + // + + SETCLASS(ArrayList_class, "java/util/ArrayList"); + SETCLASS(DataStoreGetListener_class, "com/zerotier/sdk/DataStoreGetListener"); + SETCLASS(DataStorePutListener_class, "com/zerotier/sdk/DataStorePutListener"); + SETCLASS(EventListener_class, "com/zerotier/sdk/EventListener"); + SETCLASS(Event_class, "com/zerotier/sdk/Event"); + SETCLASS(Inet4Address_class, "java/net/Inet4Address"); + SETCLASS(Inet6Address_class, "java/net/Inet6Address"); + SETCLASS(InetAddress_class, "java/net/InetAddress"); + SETCLASS(InetSocketAddress_class, "java/net/InetSocketAddress"); + SETCLASS(NodeStatus_class, "com/zerotier/sdk/NodeStatus"); + SETCLASS(Node_class, "com/zerotier/sdk/Node"); + SETCLASS(PacketSender_class, "com/zerotier/sdk/PacketSender"); + SETCLASS(PathChecker_class, "com/zerotier/sdk/PathChecker"); + SETCLASS(PeerPhysicalPath_class, "com/zerotier/sdk/PeerPhysicalPath"); + SETCLASS(PeerRole_class, "com/zerotier/sdk/PeerRole"); + SETCLASS(Peer_class, "com/zerotier/sdk/Peer"); + SETCLASS(ResultCode_class, "com/zerotier/sdk/ResultCode"); + SETCLASS(Version_class, "com/zerotier/sdk/Version"); + SETCLASS(VirtualNetworkConfigListener_class, "com/zerotier/sdk/VirtualNetworkConfigListener"); + SETCLASS(VirtualNetworkConfigOperation_class, "com/zerotier/sdk/VirtualNetworkConfigOperation"); + SETCLASS(VirtualNetworkConfig_class, "com/zerotier/sdk/VirtualNetworkConfig"); + SETCLASS(VirtualNetworkDNS_class, "com/zerotier/sdk/VirtualNetworkDNS"); + SETCLASS(VirtualNetworkFrameListener_class, "com/zerotier/sdk/VirtualNetworkFrameListener"); + SETCLASS(VirtualNetworkRoute_class, "com/zerotier/sdk/VirtualNetworkRoute"); + SETCLASS(VirtualNetworkStatus_class, "com/zerotier/sdk/VirtualNetworkStatus"); + SETCLASS(VirtualNetworkType_class, "com/zerotier/sdk/VirtualNetworkType"); + + // + // Instance methods + // + + EXCEPTIONANDNULLCHECK(ArrayList_add_method = env->GetMethodID(ArrayList_class, "add", "(Ljava/lang/Object;)Z")); + EXCEPTIONANDNULLCHECK(ArrayList_ctor = env->GetMethodID(ArrayList_class, "", "(I)V")); + EXCEPTIONANDNULLCHECK(DataStoreGetListener_onDataStoreGet_method = env->GetMethodID(DataStoreGetListener_class, "onDataStoreGet", "(Ljava/lang/String;[B)J")); + EXCEPTIONANDNULLCHECK(DataStorePutListener_onDataStorePut_method = env->GetMethodID(DataStorePutListener_class, "onDataStorePut", "(Ljava/lang/String;[BZ)I")); + EXCEPTIONANDNULLCHECK(DataStorePutListener_onDelete_method = env->GetMethodID(DataStorePutListener_class, "onDelete", "(Ljava/lang/String;)I")); + EXCEPTIONANDNULLCHECK(EventListener_onEvent_method = env->GetMethodID(EventListener_class, "onEvent", "(Lcom/zerotier/sdk/Event;)V")); + EXCEPTIONANDNULLCHECK(EventListener_onTrace_method = env->GetMethodID(EventListener_class, "onTrace", "(Ljava/lang/String;)V")); + EXCEPTIONANDNULLCHECK(InetAddress_getAddress_method = env->GetMethodID(InetAddress_class, "getAddress", "()[B")); + EXCEPTIONANDNULLCHECK(InetSocketAddress_ctor = env->GetMethodID(InetSocketAddress_class, "", "(Ljava/net/InetAddress;I)V")); + EXCEPTIONANDNULLCHECK(InetSocketAddress_getAddress_method = env->GetMethodID(InetSocketAddress_class, "getAddress", "()Ljava/net/InetAddress;")); + EXCEPTIONANDNULLCHECK(InetSocketAddress_getPort_method = env->GetMethodID(InetSocketAddress_class, "getPort", "()I")); + EXCEPTIONANDNULLCHECK(NodeStatus_ctor = env->GetMethodID(NodeStatus_class, "", "(JLjava/lang/String;Ljava/lang/String;Z)V")); + EXCEPTIONANDNULLCHECK(PacketSender_onSendPacketRequested_method = env->GetMethodID(PacketSender_class, "onSendPacketRequested", "(JLjava/net/InetSocketAddress;[BI)I")); + EXCEPTIONANDNULLCHECK(PathChecker_onPathCheck_method = env->GetMethodID(PathChecker_class, "onPathCheck", "(JJLjava/net/InetSocketAddress;)Z")); + EXCEPTIONANDNULLCHECK(PathChecker_onPathLookup_method = env->GetMethodID(PathChecker_class, "onPathLookup", "(JI)Ljava/net/InetSocketAddress;")); + EXCEPTIONANDNULLCHECK(PeerPhysicalPath_ctor = env->GetMethodID(PeerPhysicalPath_class, "", "(Ljava/net/InetSocketAddress;JJZ)V")); + EXCEPTIONANDNULLCHECK(Peer_ctor = env->GetMethodID(Peer_class, "", "(JIIIILcom/zerotier/sdk/PeerRole;[Lcom/zerotier/sdk/PeerPhysicalPath;)V")); + EXCEPTIONANDNULLCHECK(Version_ctor = env->GetMethodID(Version_class, "", "(III)V")); + EXCEPTIONANDNULLCHECK(VirtualNetworkConfigListener_onNetworkConfigurationUpdated_method = env->GetMethodID(VirtualNetworkConfigListener_class, "onNetworkConfigurationUpdated", "(JLcom/zerotier/sdk/VirtualNetworkConfigOperation;Lcom/zerotier/sdk/VirtualNetworkConfig;)I")); + EXCEPTIONANDNULLCHECK(VirtualNetworkConfig_ctor = env->GetMethodID(VirtualNetworkConfig_class, "", "(JJLjava/lang/String;Lcom/zerotier/sdk/VirtualNetworkStatus;Lcom/zerotier/sdk/VirtualNetworkType;IZZZIJ[Ljava/net/InetSocketAddress;[Lcom/zerotier/sdk/VirtualNetworkRoute;Lcom/zerotier/sdk/VirtualNetworkDNS;)V")); + EXCEPTIONANDNULLCHECK(VirtualNetworkDNS_ctor = env->GetMethodID(VirtualNetworkDNS_class, "", "(Ljava/lang/String;Ljava/util/ArrayList;)V")); + EXCEPTIONANDNULLCHECK(VirtualNetworkFrameListener_onVirtualNetworkFrame_method = env->GetMethodID(VirtualNetworkFrameListener_class, "onVirtualNetworkFrame", "(JJJJJ[B)V")); + EXCEPTIONANDNULLCHECK(VirtualNetworkRoute_ctor = env->GetMethodID(VirtualNetworkRoute_class, "", "(Ljava/net/InetSocketAddress;Ljava/net/InetSocketAddress;II)V")); + + // + // Static methods + // + + EXCEPTIONANDNULLCHECK(Event_fromInt_method = env->GetStaticMethodID(Event_class, "fromInt", "(I)Lcom/zerotier/sdk/Event;")); + EXCEPTIONANDNULLCHECK(InetAddress_getByAddress_method = env->GetStaticMethodID(InetAddress_class, "getByAddress", "([B)Ljava/net/InetAddress;")); + EXCEPTIONANDNULLCHECK(PeerRole_fromInt_method = env->GetStaticMethodID(PeerRole_class, "fromInt", "(I)Lcom/zerotier/sdk/PeerRole;")); + EXCEPTIONANDNULLCHECK(ResultCode_fromInt_method = env->GetStaticMethodID(ResultCode_class, "fromInt", "(I)Lcom/zerotier/sdk/ResultCode;")); + EXCEPTIONANDNULLCHECK(VirtualNetworkConfigOperation_fromInt_method = env->GetStaticMethodID(VirtualNetworkConfigOperation_class, "fromInt", "(I)Lcom/zerotier/sdk/VirtualNetworkConfigOperation;")); + EXCEPTIONANDNULLCHECK(VirtualNetworkStatus_fromInt_method = env->GetStaticMethodID(VirtualNetworkStatus_class, "fromInt", "(I)Lcom/zerotier/sdk/VirtualNetworkStatus;")); + EXCEPTIONANDNULLCHECK(VirtualNetworkType_fromInt_method = env->GetStaticMethodID(VirtualNetworkType_class, "fromInt", "(I)Lcom/zerotier/sdk/VirtualNetworkType;")); + + // + // Enums + // + + SETOBJECT(ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum, createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL)); + SETOBJECT(ResultCode_RESULT_OK_enum, createResultObject(env, ZT_RESULT_OK)); +} + +void teardownJNICache(JavaVM *vm) { + + JNIEnv *env; + GETENV(env, vm); + + env->DeleteGlobalRef(ArrayList_class); + env->DeleteGlobalRef(DataStoreGetListener_class); + env->DeleteGlobalRef(DataStorePutListener_class); + env->DeleteGlobalRef(EventListener_class); + env->DeleteGlobalRef(Event_class); + env->DeleteGlobalRef(InetAddress_class); + env->DeleteGlobalRef(InetSocketAddress_class); + env->DeleteGlobalRef(NodeStatus_class); + env->DeleteGlobalRef(Node_class); + env->DeleteGlobalRef(PacketSender_class); + env->DeleteGlobalRef(PathChecker_class); + env->DeleteGlobalRef(PeerPhysicalPath_class); + env->DeleteGlobalRef(PeerRole_class); + env->DeleteGlobalRef(Peer_class); + env->DeleteGlobalRef(ResultCode_class); + env->DeleteGlobalRef(Version_class); + env->DeleteGlobalRef(VirtualNetworkConfigListener_class); + env->DeleteGlobalRef(VirtualNetworkConfigOperation_class); + env->DeleteGlobalRef(VirtualNetworkConfig_class); + env->DeleteGlobalRef(VirtualNetworkDNS_class); + env->DeleteGlobalRef(VirtualNetworkFrameListener_class); + env->DeleteGlobalRef(VirtualNetworkRoute_class); + env->DeleteGlobalRef(VirtualNetworkStatus_class); + env->DeleteGlobalRef(VirtualNetworkType_class); + + env->DeleteGlobalRef(ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum); + env->DeleteGlobalRef(ResultCode_RESULT_OK_enum); +} diff --git a/java/jni/ZT_jnicache.h b/java/jni/ZT_jnicache.h new file mode 100644 index 000000000..c5cc9cb2f --- /dev/null +++ b/java/jni/ZT_jnicache.h @@ -0,0 +1,92 @@ +// +// Created by Brenton Bostick on 1/18/23. +// + +#ifndef ZEROTIERANDROID_JNICACHE_H +#define ZEROTIERANDROID_JNICACHE_H + +#include + + +// +// Classes +// + +extern jclass ArrayList_class; +extern jclass DataStoreGetListener_class; +extern jclass DataStorePutListener_class; +extern jclass EventListener_class; +extern jclass Event_class; +extern jclass Inet4Address_class; +extern jclass Inet6Address_class; +extern jclass InetAddress_class; +extern jclass InetSocketAddress_class; +extern jclass NodeStatus_class; +extern jclass Node_class; +extern jclass PacketSender_class; +extern jclass PathChecker_class; +extern jclass PeerPhysicalPath_class; +extern jclass PeerRole_class; +extern jclass Peer_class; +extern jclass ResultCode_class; +extern jclass Version_class; +extern jclass VirtualNetworkConfigListener_class; +extern jclass VirtualNetworkConfigOperation_class; +extern jclass VirtualNetworkConfig_class; +extern jclass VirtualNetworkDNS_class; +extern jclass VirtualNetworkFrameListener_class; +extern jclass VirtualNetworkRoute_class; +extern jclass VirtualNetworkStatus_class; +extern jclass VirtualNetworkType_class; + +// +// Instance methods +// + +extern jmethodID ArrayList_add_method; +extern jmethodID ArrayList_ctor; +extern jmethodID DataStoreGetListener_onDataStoreGet_method; +extern jmethodID DataStorePutListener_onDataStorePut_method; +extern jmethodID DataStorePutListener_onDelete_method; +extern jmethodID EventListener_onEvent_method; +extern jmethodID EventListener_onTrace_method; +extern jmethodID InetAddress_getAddress_method; +extern jmethodID InetSocketAddress_ctor; +extern jmethodID InetSocketAddress_getAddress_method; +extern jmethodID InetSocketAddress_getPort_method; +extern jmethodID NodeStatus_ctor; +extern jmethodID PacketSender_onSendPacketRequested_method; +extern jmethodID PathChecker_onPathCheck_method; +extern jmethodID PathChecker_onPathLookup_method; +extern jmethodID PeerPhysicalPath_ctor; +extern jmethodID Peer_ctor; +extern jmethodID Version_ctor; +extern jmethodID VirtualNetworkConfigListener_onNetworkConfigurationUpdated_method; +extern jmethodID VirtualNetworkConfig_ctor; +extern jmethodID VirtualNetworkDNS_ctor; +extern jmethodID VirtualNetworkFrameListener_onVirtualNetworkFrame_method; +extern jmethodID VirtualNetworkRoute_ctor; + +// +// Static methods +// + +extern jmethodID Event_fromInt_method; +extern jmethodID InetAddress_getByAddress_method; +extern jmethodID PeerRole_fromInt_method; +extern jmethodID ResultCode_fromInt_method; +extern jmethodID VirtualNetworkConfigOperation_fromInt_method; +extern jmethodID VirtualNetworkStatus_fromInt_method; +extern jmethodID VirtualNetworkType_fromInt_method; + +// +// Enums +// + +extern jobject ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; +extern jobject ResultCode_RESULT_OK_enum; + +void setupJNICache(JavaVM *vm); +void teardownJNICache(JavaVM *vm); + +#endif // ZEROTIERANDROID_JNICACHE_H diff --git a/java/jni/ZT_jnilookup.cpp b/java/jni/ZT_jnilookup.cpp deleted file mode 100644 index 4d867a35c..000000000 --- a/java/jni/ZT_jnilookup.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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 . - * - * -- - * - * 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 "ZT_jnilookup.h" -#include "ZT_jniutils.h" - -JniLookup::JniLookup() - : m_jvm(NULL) -{ - LOGV("JNI Cache Created"); -} - -JniLookup::JniLookup(JavaVM *jvm) - : m_jvm(jvm) -{ - LOGV("JNI Cache Created"); -} - -JniLookup::~JniLookup() -{ - LOGV("JNI Cache Destroyed"); -} - - -void JniLookup::setJavaVM(JavaVM *jvm) -{ - LOGV("Assigned JVM to object"); - m_jvm = jvm; -} - - -jclass JniLookup::findClass(const std::string &name) -{ - if(!m_jvm) - return NULL; - - // get the class from the JVM - JNIEnv *env = NULL; - if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) - { - LOGE("Error retrieving JNI Environment"); - return NULL; - } - const char *c = name.c_str(); - jclass cls = env->FindClass(c); - if(env->ExceptionCheck()) - { - LOGE("Error finding class: %s", name.c_str()); - return NULL; - } - - return cls; -} - - -jmethodID JniLookup::findMethod(jclass cls, const std::string &methodName, const std::string &methodSig) -{ - if(!m_jvm) - return NULL; - - 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; - } - - return mid; -} - -jmethodID JniLookup::findStaticMethod(jclass cls, const std::string &methodName, const std::string &methodSig) -{ - if(!m_jvm) - return NULL; - - 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; - } - - return mid; -} - -jfieldID JniLookup::findField(jclass cls, const std::string &fieldName, const std::string &typeStr) -{ - if(!m_jvm) - return NULL; - - 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; - } - - return fid; -} - -jfieldID JniLookup::findStaticField(jclass cls, const std::string &fieldName, const std::string &typeStr) -{ - if(!m_jvm) - return NULL; - - 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; - } - - return fid; -} \ No newline at end of file diff --git a/java/jni/ZT_jnilookup.h b/java/jni/ZT_jnilookup.h deleted file mode 100644 index f5bd97d7d..000000000 --- a/java/jni/ZT_jnilookup.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 . - * - * -- - * - * 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 ZT_JNILOOKUP_H_ -#define ZT_JNILOOKUP_H_ - -#include -#include -#include - - - -class JniLookup { -public: - JniLookup(); - JniLookup(JavaVM *jvm); - ~JniLookup(); - - void setJavaVM(JavaVM *jvm); - - 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: - JavaVM *m_jvm; -}; - -#endif \ No newline at end of file diff --git a/java/jni/ZT_jniutils.cpp b/java/jni/ZT_jniutils.cpp index c479f87e2..053c5adb0 100644 --- a/java/jni/ZT_jniutils.cpp +++ b/java/jni/ZT_jniutils.cpp @@ -17,322 +17,123 @@ */ #include "ZT_jniutils.h" -#include "ZT_jnilookup.h" -#include "ZT_jniarray.h" + +#include "ZT_jnicache.h" #include -#include +#include #include #include #include -#include -extern JniLookup lookup; - -#ifdef __cplusplus -extern "C" { -#endif +#define LOG_TAG "Utils" jobject createResultObject(JNIEnv *env, ZT_ResultCode code) { - jclass resultClass = NULL; - - jobject resultObject = NULL; - - resultClass = lookup.findClass("com/zerotier/sdk/ResultCode"); - if(resultClass == NULL) - { - LOGE("Couldn't find ResultCode class"); - return NULL; // exception thrown - } - - std::string fieldName; - switch(code) - { - case ZT_RESULT_OK: - case ZT_RESULT_OK_IGNORED: - LOGV("ZT_RESULT_OK"); - fieldName = "RESULT_OK"; - break; - case ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY: - LOGV("ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY"); - fieldName = "RESULT_FATAL_ERROR_OUT_OF_MEMORY"; - break; - case ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED: - LOGV("RESULT_FATAL_ERROR_DATA_STORE_FAILED"); - fieldName = "RESULT_FATAL_ERROR_DATA_STORE_FAILED"; - break; - case ZT_RESULT_ERROR_NETWORK_NOT_FOUND: - LOGV("ZT_RESULT_ERROR_NETWORK_NOT_FOUND"); - fieldName = "RESULT_ERROR_NETWORK_NOT_FOUND"; - break; - case ZT_RESULT_ERROR_UNSUPPORTED_OPERATION: - LOGV("ZT_RESULT_ERROR_UNSUPPORTED_OPERATION"); - fieldName = "RESULT_ERROR_UNSUPPORTED_OPERATION"; - break; - case ZT_RESULT_ERROR_BAD_PARAMETER: - LOGV("ZT_RESULT_ERROR_BAD_PARAMETER"); - fieldName = "ZT_RESULT_ERROR_BAD_PARAMETER"; - break; - case ZT_RESULT_FATAL_ERROR_INTERNAL: - default: - LOGV("ZT_RESULT_FATAL_ERROR_INTERNAL"); - fieldName = "RESULT_FATAL_ERROR_INTERNAL"; - break; - } - - jfieldID enumField = lookup.findStaticField(resultClass, fieldName.c_str(), "Lcom/zerotier/sdk/ResultCode;"); - if(env->ExceptionCheck() || enumField == NULL) - { - LOGE("Error on FindStaticField"); + jobject resultObject = env->CallStaticObjectMethod(ResultCode_class, ResultCode_fromInt_method, code); + if(env->ExceptionCheck() || resultObject == NULL) { + LOGE("Error creating ResultCode object"); return NULL; } - resultObject = env->GetStaticObjectField(resultClass, enumField); - if(env->ExceptionCheck() || resultObject == NULL) - { - LOGE("Error on GetStaticObjectField"); - } return resultObject; } jobject createVirtualNetworkStatus(JNIEnv *env, ZT_VirtualNetworkStatus status) { - jobject statusObject = NULL; - - jclass statusClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkStatus"); - if(statusClass == NULL) - { - return NULL; // exception thrown + jobject statusObject = env->CallStaticObjectMethod(VirtualNetworkStatus_class, VirtualNetworkStatus_fromInt_method, status); + if (env->ExceptionCheck() || statusObject == NULL) { + LOGE("Error creating VirtualNetworkStatus object"); + return NULL; } - std::string fieldName; - switch(status) - { - case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION: - fieldName = "NETWORK_STATUS_REQUESTING_CONFIGURATION"; - break; - case ZT_NETWORK_STATUS_OK: - fieldName = "NETWORK_STATUS_OK"; - break; - case ZT_NETWORK_STATUS_AUTHENTICATION_REQUIRED: - fieldName = "NETWORK_STATUS_AUTHENTICATION_REQUIRED"; - break; - case ZT_NETWORK_STATUS_ACCESS_DENIED: - fieldName = "NETWORK_STATUS_ACCESS_DENIED"; - break; - case ZT_NETWORK_STATUS_NOT_FOUND: - fieldName = "NETWORK_STATUS_NOT_FOUND"; - break; - case ZT_NETWORK_STATUS_PORT_ERROR: - fieldName = "NETWORK_STATUS_PORT_ERROR"; - break; - case ZT_NETWORK_STATUS_CLIENT_TOO_OLD: - fieldName = "NETWORK_STATUS_CLIENT_TOO_OLD"; - break; - } - - jfieldID enumField = lookup.findStaticField(statusClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkStatus;"); - - statusObject = env->GetStaticObjectField(statusClass, enumField); - return statusObject; } jobject createEvent(JNIEnv *env, ZT_Event event) { - jclass eventClass = NULL; - jobject eventObject = NULL; - - eventClass = lookup.findClass("com/zerotier/sdk/Event"); - if(eventClass == NULL) - { + jobject eventObject = env->CallStaticObjectMethod(Event_class, Event_fromInt_method, event); + if (env->ExceptionCheck() || eventObject == NULL) { + LOGE("Error creating Event object"); return NULL; } - std::string fieldName; - switch(event) - { - case ZT_EVENT_UP: - fieldName = "EVENT_UP"; - break; - case ZT_EVENT_OFFLINE: - fieldName = "EVENT_OFFLINE"; - break; - case ZT_EVENT_ONLINE: - fieldName = "EVENT_ONLINE"; - break; - case ZT_EVENT_DOWN: - fieldName = "EVENT_DOWN"; - break; - case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION: - fieldName = "EVENT_FATAL_ERROR_IDENTITY_COLLISION"; - break; - case ZT_EVENT_TRACE: - fieldName = "EVENT_TRACE"; - break; - case ZT_EVENT_USER_MESSAGE: - break; - case ZT_EVENT_REMOTE_TRACE: - default: - break; - } - - jfieldID enumField = lookup.findStaticField(eventClass, fieldName.c_str(), "Lcom/zerotier/sdk/Event;"); - - eventObject = env->GetStaticObjectField(eventClass, enumField); - return eventObject; } jobject createPeerRole(JNIEnv *env, ZT_PeerRole role) { - jclass peerRoleClass = NULL; - jobject peerRoleObject = NULL; - - peerRoleClass = lookup.findClass("com/zerotier/sdk/PeerRole"); - if(peerRoleClass == NULL) - { + jobject peerRoleObject = env->CallStaticObjectMethod(PeerRole_class, PeerRole_fromInt_method, role); + if (env->ExceptionCheck() || peerRoleObject == NULL) { + LOGE("Error creating PeerRole object"); return NULL; } - std::string fieldName; - switch(role) - { - case ZT_PEER_ROLE_LEAF: - fieldName = "PEER_ROLE_LEAF"; - break; - case ZT_PEER_ROLE_MOON: - fieldName = "PEER_ROLE_MOON"; - break; - case ZT_PEER_ROLE_PLANET: - fieldName = "PEER_ROLE_PLANET"; - break; - } - - jfieldID enumField = lookup.findStaticField(peerRoleClass, fieldName.c_str(), "Lcom/zerotier/sdk/PeerRole;"); - - peerRoleObject = env->GetStaticObjectField(peerRoleClass, enumField); - return peerRoleObject; } jobject createVirtualNetworkType(JNIEnv *env, ZT_VirtualNetworkType type) { - jclass vntypeClass = NULL; - jobject vntypeObject = NULL; - - vntypeClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkType"); - if(env->ExceptionCheck() || vntypeClass == NULL) - { + jobject vntypeObject = env->CallStaticObjectMethod(VirtualNetworkType_class, VirtualNetworkType_fromInt_method, type); + if (env->ExceptionCheck() || vntypeObject == NULL) { + LOGE("Error creating VirtualNetworkType object"); return NULL; } - std::string fieldName; - switch(type) - { - case ZT_NETWORK_TYPE_PRIVATE: - fieldName = "NETWORK_TYPE_PRIVATE"; - break; - case ZT_NETWORK_TYPE_PUBLIC: - fieldName = "NETWORK_TYPE_PUBLIC"; - break; - } - - jfieldID enumField = lookup.findStaticField(vntypeClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkType;"); - vntypeObject = env->GetStaticObjectField(vntypeClass, enumField); return vntypeObject; } jobject createVirtualNetworkConfigOperation(JNIEnv *env, ZT_VirtualNetworkConfigOperation op) { - jclass vnetConfigOpClass = NULL; - jobject vnetConfigOpObject = NULL; - - vnetConfigOpClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfigOperation"); - if(env->ExceptionCheck() || vnetConfigOpClass == NULL) - { + jobject vnetConfigOpObject = env->CallStaticObjectMethod(VirtualNetworkConfigOperation_class, VirtualNetworkConfigOperation_fromInt_method, op); + if (env->ExceptionCheck() || vnetConfigOpObject == NULL) { + LOGE("Error creating VirtualNetworkConfigOperation object"); return NULL; } - std::string fieldName; - switch(op) - { - case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP: - fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_UP"; - break; - case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE: - fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE"; - break; - case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN: - fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN"; - break; - case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY: - fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY"; - break; - } - - jfieldID enumField = lookup.findStaticField(vnetConfigOpClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkConfigOperation;"); - vnetConfigOpObject = env->GetStaticObjectField(vnetConfigOpClass, enumField); return vnetConfigOpObject; } jobject newInetAddress(JNIEnv *env, const sockaddr_storage &addr) { - LOGV("newInetAddress"); - jclass inetAddressClass = NULL; - jmethodID inetAddress_getByAddress = NULL; - - inetAddressClass = lookup.findClass("java/net/InetAddress"); - if(env->ExceptionCheck() || inetAddressClass == NULL) - { - LOGE("Error finding InetAddress class"); - return NULL; - } - - inetAddress_getByAddress = lookup.findStaticMethod( - inetAddressClass, "getByAddress", "([B)Ljava/net/InetAddress;"); - if(env->ExceptionCheck() || inetAddress_getByAddress == NULL) - { - LOGE("Error finding getByAddress() static method"); - 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) + const unsigned char *bytes = reinterpret_cast(&ipv6->sin6_addr.s6_addr); + + jbyteArray buff = newByteArray(env, bytes, 16); + if(env->ExceptionCheck() || buff == NULL) { - LOGE("Error creating IPV6 byte array"); return NULL; } - env->SetByteArrayRegion(buff, 0, 16, (jbyte*)ipv6->sin6_addr.s6_addr); inetAddressObj = env->CallStaticObjectMethod( - inetAddressClass, inetAddress_getByAddress, buff); + InetAddress_class, InetAddress_getByAddress_method, buff); } break; case AF_INET: { sockaddr_in *ipv4 = (sockaddr_in*)&addr; - jbyteArray buff = env->NewByteArray(4); - if(buff == NULL) + const unsigned char *bytes = reinterpret_cast(&ipv4->sin_addr.s_addr); + jbyteArray buff = newByteArray(env, bytes, 4); + if(env->ExceptionCheck() || buff == NULL) { - LOGE("Error creating IPV4 byte array"); return NULL; } - env->SetByteArrayRegion(buff, 0, 4, (jbyte*)&ipv4->sin_addr); inetAddressObj = env->CallStaticObjectMethod( - inetAddressClass, inetAddress_getByAddress, buff); + InetAddress_class, InetAddress_getByAddress_method, buff); } break; + default: + { + assert(false && "addr.ss_family is neither AF_INET6 nor AF_INET"); + } } if(env->ExceptionCheck() || inetAddressObj == NULL) { LOGE("Error creating InetAddress object"); @@ -342,693 +143,483 @@ jobject newInetAddress(JNIEnv *env, const sockaddr_storage &addr) return inetAddressObj; } -jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr) -{ - LOGV("newInetSocketAddress Called"); - jclass inetSocketAddressClass = NULL; - jmethodID inetSocketAddress_constructor = NULL; - - inetSocketAddressClass = lookup.findClass("java/net/InetSocketAddress"); - if(env->ExceptionCheck() || inetSocketAddressClass == NULL) - { - LOGE("Error finding InetSocketAddress Class"); - return NULL; - } - - jobject inetAddressObject = NULL; - - if(addr.ss_family != 0) - { - inetAddressObject = newInetAddress(env, addr); - - if(env->ExceptionCheck() || inetAddressObject == NULL) - { - LOGE("Error creating new inet address"); - return NULL; - } - } - else - { - return NULL; - } - - inetSocketAddress_constructor = lookup.findMethod( - inetSocketAddressClass, "", "(Ljava/net/InetAddress;I)V"); - if(env->ExceptionCheck() || inetSocketAddress_constructor == NULL) - { - LOGE("Error finding InetSocketAddress constructor"); - return NULL; - } +int addressPort(const sockaddr_storage addr) { int port = 0; switch(addr.ss_family) { case AF_INET6: { - LOGV("IPV6 Address"); sockaddr_in6 *ipv6 = (sockaddr_in6*)&addr; port = ntohs(ipv6->sin6_port); - LOGV("Port %d", port); } - break; + break; case AF_INET: { - LOGV("IPV4 Address"); sockaddr_in *ipv4 = (sockaddr_in*)&addr; port = ntohs(ipv4->sin_port); - LOGV("Port: %d", port); } - break; + break; default: { - break; + assert(false && "addr.ss_family is neither AF_INET6 nor AF_INET"); } } + return port; +} - jobject inetSocketAddressObject = env->NewObject(inetSocketAddressClass, inetSocketAddress_constructor, inetAddressObject, port); +// +// addr may be empty +// +// may return NULL +// +jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr) +{ + if(isSocketAddressEmpty(addr)) + { + return NULL; + } + + jobject inetAddressObject = newInetAddress(env, addr); + + if(env->ExceptionCheck() || inetAddressObject == NULL) + { + return NULL; + } + + int port = addressPort(addr); + + jobject inetSocketAddressObject = env->NewObject(InetSocketAddress_class, InetSocketAddress_ctor, inetAddressObject, port); if(env->ExceptionCheck() || inetSocketAddressObject == NULL) { LOGE("Error creating InetSocketAddress object"); + return NULL; } return inetSocketAddressObject; } jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp) { - LOGV("newPeerPhysicalPath Called"); - jclass pppClass = NULL; - - jfieldID addressField = NULL; - jfieldID lastSendField = NULL; - jfieldID lastReceiveField = NULL; - jfieldID preferredField = NULL; - - jmethodID ppp_constructor = NULL; - - pppClass = lookup.findClass("com/zerotier/sdk/PeerPhysicalPath"); - if(env->ExceptionCheck() || pppClass == NULL) - { - LOGE("Error finding PeerPhysicalPath class"); + // + // may be NULL + // + jobject addressObject = newInetSocketAddress(env, ppp.address); + if(env->ExceptionCheck()) { return NULL; } - addressField = lookup.findField(pppClass, "address", "Ljava/net/InetSocketAddress;"); - if(env->ExceptionCheck() || addressField == NULL) - { - LOGE("Error finding address field"); - return NULL; - } - - lastSendField = lookup.findField(pppClass, "lastSend", "J"); - if(env->ExceptionCheck() || lastSendField == NULL) - { - LOGE("Error finding lastSend field"); - return NULL; - } - - lastReceiveField = lookup.findField(pppClass, "lastReceive", "J"); - if(env->ExceptionCheck() || lastReceiveField == NULL) - { - LOGE("Error finding lastReceive field"); - return NULL; - } - - preferredField = lookup.findField(pppClass, "preferred", "Z"); - if(env->ExceptionCheck() || preferredField == NULL) - { - LOGE("Error finding preferred field"); - return NULL; - } - - ppp_constructor = lookup.findMethod(pppClass, "", "()V"); - if(env->ExceptionCheck() || ppp_constructor == NULL) - { - LOGE("Error finding PeerPhysicalPath constructor"); - return NULL; - } - - jobject pppObject = env->NewObject(pppClass, ppp_constructor); + jobject pppObject = env->NewObject( + PeerPhysicalPath_class, + PeerPhysicalPath_ctor, + addressObject, + ppp.lastSend, + ppp.lastReceive, + ppp.preferred); if(env->ExceptionCheck() || pppObject == NULL) { LOGE("Error creating PPP object"); - return NULL; // out of memory - } - - jobject addressObject = newInetSocketAddress(env, ppp.address); - if(env->ExceptionCheck() || addressObject == NULL) { - LOGE("Error creating InetSocketAddress object"); return NULL; } - env->SetObjectField(pppObject, addressField, addressObject); - env->SetLongField(pppObject, lastSendField, ppp.lastSend); - env->SetLongField(pppObject, lastReceiveField, ppp.lastReceive); - env->SetBooleanField(pppObject, preferredField, ppp.preferred); - - if(env->ExceptionCheck()) { - LOGE("Exception assigning fields to PeerPhysicalPath object"); - } - return pppObject; } jobject newPeer(JNIEnv *env, const ZT_Peer &peer) { - LOGV("newPeer called"); - - jclass peerClass = NULL; - - jfieldID addressField = NULL; - jfieldID versionMajorField = NULL; - jfieldID versionMinorField = NULL; - jfieldID versionRevField = NULL; - jfieldID latencyField = NULL; - jfieldID roleField = NULL; - jfieldID pathsField = NULL; - - jmethodID peer_constructor = NULL; - - peerClass = lookup.findClass("com/zerotier/sdk/Peer"); - if(env->ExceptionCheck() || peerClass == NULL) + jobject peerRoleObj = createPeerRole(env, peer.role); + if(env->ExceptionCheck() || peerRoleObj == NULL) { - LOGE("Error finding Peer class"); - return NULL; - } - - addressField = lookup.findField(peerClass, "address", "J"); - if(env->ExceptionCheck() || addressField == NULL) - { - LOGE("Error finding address field of Peer object"); - return NULL; - } - - versionMajorField = lookup.findField(peerClass, "versionMajor", "I"); - if(env->ExceptionCheck() || versionMajorField == NULL) - { - LOGE("Error finding versionMajor field of Peer object"); - return NULL; - } - - versionMinorField = lookup.findField(peerClass, "versionMinor", "I"); - if(env->ExceptionCheck() || versionMinorField == NULL) - { - LOGE("Error finding versionMinor field of Peer object"); - return NULL; - } - - versionRevField = lookup.findField(peerClass, "versionRev", "I"); - if(env->ExceptionCheck() || versionRevField == NULL) - { - LOGE("Error finding versionRev field of Peer object"); - return NULL; - } - - latencyField = lookup.findField(peerClass, "latency", "I"); - if(env->ExceptionCheck() || latencyField == NULL) - { - LOGE("Error finding latency field of Peer object"); - return NULL; - } - - roleField = lookup.findField(peerClass, "role", "Lcom/zerotier/sdk/PeerRole;"); - if(env->ExceptionCheck() || roleField == NULL) - { - LOGE("Error finding role field of Peer object"); - return NULL; - } - - pathsField = lookup.findField(peerClass, "paths", "[Lcom/zerotier/sdk/PeerPhysicalPath;"); - if(env->ExceptionCheck() || pathsField == NULL) - { - LOGE("Error finding paths field of Peer object"); - return NULL; - } - - peer_constructor = lookup.findMethod(peerClass, "", "()V"); - if(env->ExceptionCheck() || peer_constructor == NULL) - { - LOGE("Error finding Peer constructor"); - return NULL; - } - - jobject peerObject = env->NewObject(peerClass, peer_constructor); - if(env->ExceptionCheck() || peerObject == NULL) - { - LOGE("Error creating Peer object"); return NULL; // out of memory } - env->SetLongField(peerObject, addressField, (jlong)peer.address); - env->SetIntField(peerObject, versionMajorField, peer.versionMajor); - env->SetIntField(peerObject, versionMinorField, peer.versionMinor); - env->SetIntField(peerObject, versionRevField, peer.versionRev); - env->SetIntField(peerObject, latencyField, peer.latency); - env->SetObjectField(peerObject, roleField, createPeerRole(env, peer.role)); - - jclass peerPhysicalPathClass = lookup.findClass("com/zerotier/sdk/PeerPhysicalPath"); - if(env->ExceptionCheck() || peerPhysicalPathClass == NULL) - { - LOGE("Error finding PeerPhysicalPath class"); + jobjectArray arrayObject = newPeerPhysicalPathArray(env, peer.paths, peer.pathCount); + if (env->ExceptionCheck() || arrayObject == NULL) { return NULL; } - jobjectArray arrayObject = env->NewObjectArray( - peer.pathCount, peerPhysicalPathClass, NULL); - if(env->ExceptionCheck() || arrayObject == NULL) + jobject peerObject = env->NewObject( + Peer_class, + Peer_ctor, + peer.address, + peer.versionMajor, + peer.versionMinor, + peer.versionRev, + peer.latency, + peerRoleObj, + arrayObject); + if(env->ExceptionCheck() || peerObject == NULL) { - LOGE("Error creating PeerPhysicalPath[] array"); + LOGE("Error creating Peer object"); return NULL; } - for(unsigned int i = 0; i < peer.pathCount; ++i) - { - jobject path = newPeerPhysicalPath(env, peer.paths[i]); - - env->SetObjectArrayElement(arrayObject, i, path); - if(env->ExceptionCheck()) { - LOGE("exception assigning PeerPhysicalPath to array"); - break; - } - } - - env->SetObjectField(peerObject, pathsField, arrayObject); - return peerObject; } jobject newNetworkConfig(JNIEnv *env, const ZT_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 netconfRevisionField = NULL; - jfieldID assignedAddressesField = NULL; - jfieldID routesField = NULL; - jfieldID dnsField = NULL; - - vnetConfigClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfig"); - if(vnetConfigClass == NULL) - { - LOGE("Couldn't find com.zerotier.sdk.VirtualNetworkConfig"); - return NULL; - } - - vnetConfig_constructor = lookup.findMethod( - vnetConfigClass, "", "()V"); - if(env->ExceptionCheck() || vnetConfig_constructor == NULL) - { - LOGE("Couldn't find VirtualNetworkConfig Constructor"); - return NULL; - } - - jobject vnetConfigObj = env->NewObject(vnetConfigClass, vnetConfig_constructor); - if(env->ExceptionCheck() || vnetConfigObj == NULL) - { - LOGE("Error creating new VirtualNetworkConfig object"); - return NULL; - } - - nwidField = lookup.findField(vnetConfigClass, "nwid", "J"); - if(env->ExceptionCheck() || nwidField == NULL) - { - LOGE("Error getting nwid field"); - return NULL; - } - - macField = lookup.findField(vnetConfigClass, "mac", "J"); - if(env->ExceptionCheck() || macField == NULL) - { - LOGE("Error getting mac field"); - return NULL; - } - - nameField = lookup.findField(vnetConfigClass, "name", "Ljava/lang/String;"); - if(env->ExceptionCheck() || nameField == NULL) - { - LOGE("Error getting name field"); - return NULL; - } - - statusField = lookup.findField(vnetConfigClass, "status", "Lcom/zerotier/sdk/VirtualNetworkStatus;"); - if(env->ExceptionCheck() || statusField == NULL) - { - LOGE("Error getting status field"); - return NULL; - } - - typeField = lookup.findField(vnetConfigClass, "type", "Lcom/zerotier/sdk/VirtualNetworkType;"); - if(env->ExceptionCheck() || typeField == NULL) - { - LOGE("Error getting type field"); - return NULL; - } - - mtuField = lookup.findField(vnetConfigClass, "mtu", "I"); - if(env->ExceptionCheck() || mtuField == NULL) - { - LOGE("Error getting mtu field"); - return NULL; - } - - dhcpField = lookup.findField(vnetConfigClass, "dhcp", "Z"); - if(env->ExceptionCheck() || dhcpField == NULL) - { - LOGE("Error getting dhcp field"); - return NULL; - } - - bridgeField = lookup.findField(vnetConfigClass, "bridge", "Z"); - if(env->ExceptionCheck() || bridgeField == NULL) - { - LOGE("Error getting bridge field"); - return NULL; - } - - broadcastEnabledField = lookup.findField(vnetConfigClass, "broadcastEnabled", "Z"); - if(env->ExceptionCheck() || broadcastEnabledField == NULL) - { - LOGE("Error getting broadcastEnabled field"); - return NULL; - } - - portErrorField = lookup.findField(vnetConfigClass, "portError", "I"); - if(env->ExceptionCheck() || portErrorField == NULL) - { - LOGE("Error getting portError field"); - return NULL; - } - - netconfRevisionField = lookup.findField(vnetConfigClass, "netconfRevision", "J"); - if(env->ExceptionCheck() || netconfRevisionField == NULL) - { - LOGE("Error getting netconfRevision field"); - return NULL; - } - - assignedAddressesField = lookup.findField(vnetConfigClass, "assignedAddresses", - "[Ljava/net/InetSocketAddress;"); - if(env->ExceptionCheck() || assignedAddressesField == NULL) - { - LOGE("Error getting assignedAddresses field"); - return NULL; - } - - routesField = lookup.findField(vnetConfigClass, "routes", - "[Lcom/zerotier/sdk/VirtualNetworkRoute;"); - if(env->ExceptionCheck() || routesField == NULL) - { - LOGE("Error getting routes field"); - return NULL; - } - - dnsField = lookup.findField(vnetConfigClass, "dns", "Lcom/zerotier/sdk/VirtualNetworkDNS;"); - if(env->ExceptionCheck() || dnsField == NULL) - { - LOGE("Error getting DNS field"); - return NULL; - } - - env->SetLongField(vnetConfigObj, nwidField, vnetConfig.nwid); - env->SetLongField(vnetConfigObj, macField, vnetConfig.mac); jstring nameStr = env->NewStringUTF(vnetConfig.name); if(env->ExceptionCheck() || nameStr == NULL) { + LOGE("Exception creating new string"); return NULL; // out of memory } - env->SetObjectField(vnetConfigObj, nameField, nameStr); jobject statusObject = createVirtualNetworkStatus(env, vnetConfig.status); if(env->ExceptionCheck() || statusObject == NULL) { return NULL; } - env->SetObjectField(vnetConfigObj, statusField, statusObject); jobject typeObject = createVirtualNetworkType(env, vnetConfig.type); if(env->ExceptionCheck() || typeObject == NULL) { return NULL; } - env->SetObjectField(vnetConfigObj, typeField, typeObject); - env->SetIntField(vnetConfigObj, mtuField, (int)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); - - jclass inetSocketAddressClass = lookup.findClass("java/net/InetSocketAddress"); - if(env->ExceptionCheck() || inetSocketAddressClass == NULL) - { - LOGE("Error finding InetSocketAddress class"); + jobjectArray assignedAddrArrayObj = newInetSocketAddressArray(env, vnetConfig.assignedAddresses, vnetConfig.assignedAddressCount); + if (env->ExceptionCheck() || assignedAddrArrayObj == NULL) { return NULL; } - jobjectArray assignedAddrArrayObj = env->NewObjectArray( - vnetConfig.assignedAddressCount, inetSocketAddressClass, NULL); - if(env->ExceptionCheck() || assignedAddrArrayObj == NULL) - { - LOGE("Error creating InetSocketAddress[] array"); + jobjectArray routesArrayObj = newVirtualNetworkRouteArray(env, vnetConfig.routes, vnetConfig.routeCount); + if (env->ExceptionCheck() || routesArrayObj == NULL) { return NULL; } - for(unsigned int i = 0; i < vnetConfig.assignedAddressCount; ++i) - { - jobject inetAddrObj = newInetSocketAddress(env, vnetConfig.assignedAddresses[i]); - env->SetObjectArrayElement(assignedAddrArrayObj, i, inetAddrObj); - if(env->ExceptionCheck()) - { - LOGE("Error assigning InetSocketAddress to array"); - return NULL; - } - } - - env->SetObjectField(vnetConfigObj, assignedAddressesField, assignedAddrArrayObj); - - jclass virtualNetworkRouteClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkRoute"); - if(env->ExceptionCheck() || virtualNetworkRouteClass == NULL) - { - LOGE("Error finding VirtualNetworkRoute class"); - return NULL; - } - - jobjectArray routesArrayObj = env->NewObjectArray( - vnetConfig.routeCount, virtualNetworkRouteClass, NULL); - if(env->ExceptionCheck() || routesArrayObj == NULL) - { - LOGE("Error creating VirtualNetworkRoute[] array"); - return NULL; - } - - for(unsigned int i = 0; i < vnetConfig.routeCount; ++i) - { - jobject routeObj = newVirtualNetworkRoute(env, vnetConfig.routes[i]); - env->SetObjectArrayElement(routesArrayObj, i, routeObj); - if(env->ExceptionCheck()) - { - LOGE("Error assigning VirtualNetworkRoute to array"); - return NULL; - } - } - - env->SetObjectField(vnetConfigObj, routesField, routesArrayObj); - + // + // may be NULL + // jobject dnsObj = newVirtualNetworkDNS(env, vnetConfig.dns); - if (dnsObj != NULL) { - env->SetObjectField(vnetConfigObj, dnsField, dnsObj); + if(env->ExceptionCheck()) { + return NULL; } + + jobject vnetConfigObj = env->NewObject( + VirtualNetworkConfig_class, + VirtualNetworkConfig_ctor, + vnetConfig.nwid, + vnetConfig.mac, + nameStr, + statusObject, + typeObject, + vnetConfig.mtu, + vnetConfig.dhcp, + vnetConfig.bridge, + vnetConfig.broadcastEnabled, + vnetConfig.portError, + vnetConfig.netconfRevision, + assignedAddrArrayObj, + routesArrayObj, + dnsObj); + if(env->ExceptionCheck() || vnetConfigObj == NULL) + { + LOGE("Error creating new VirtualNetworkConfig object"); + return NULL; + } + return vnetConfigObj; } jobject newVersion(JNIEnv *env, int major, int minor, int rev) { - // create a com.zerotier.sdk.Version object - jclass versionClass = NULL; - jmethodID versionConstructor = NULL; - - versionClass = lookup.findClass("com/zerotier/sdk/Version"); - if(env->ExceptionCheck() || versionClass == NULL) - { - return NULL; - } - - versionConstructor = lookup.findMethod( - versionClass, "", "()V"); - if(env->ExceptionCheck() || versionConstructor == NULL) - { - return NULL; - } - - jobject versionObj = env->NewObject(versionClass, versionConstructor); + // create a com.zerotier.sdk.Version object + jobject versionObj = env->NewObject(Version_class, Version_ctor, major, minor, rev); if(env->ExceptionCheck() || versionObj == NULL) { + LOGE("Error creating new Version object"); return NULL; } - // copy data to Version object - jfieldID majorField = NULL; - jfieldID minorField = NULL; - jfieldID revisionField = NULL; - - majorField = lookup.findField(versionClass, "major", "I"); - if(env->ExceptionCheck() || majorField == NULL) - { - return NULL; - } - - minorField = lookup.findField(versionClass, "minor", "I"); - if(env->ExceptionCheck() || minorField == NULL) - { - return NULL; - } - - revisionField = lookup.findField(versionClass, "revision", "I"); - if(env->ExceptionCheck() || revisionField == NULL) - { - return NULL; - } - - env->SetIntField(versionObj, majorField, (jint)major); - env->SetIntField(versionObj, minorField, (jint)minor); - env->SetIntField(versionObj, revisionField, (jint)rev); - return versionObj; } jobject newVirtualNetworkRoute(JNIEnv *env, const ZT_VirtualNetworkRoute &route) { - jclass virtualNetworkRouteClass = NULL; - jmethodID routeConstructor = NULL; - - virtualNetworkRouteClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkRoute"); - if(env->ExceptionCheck() || virtualNetworkRouteClass == NULL) - { + // + // may be NULL + // + jobject targetObj = newInetSocketAddress(env, route.target); + if (env->ExceptionCheck()) { return NULL; } - routeConstructor = lookup.findMethod(virtualNetworkRouteClass, "", "()V"); - if(env->ExceptionCheck() || routeConstructor == NULL) - { + // + // may be NULL + // + jobject viaObj = newInetSocketAddress(env, route.via); + if (env->ExceptionCheck()) { return NULL; } - jobject routeObj = env->NewObject(virtualNetworkRouteClass, routeConstructor); + jobject routeObj = env->NewObject( + VirtualNetworkRoute_class, + VirtualNetworkRoute_ctor, + targetObj, + viaObj, + route.flags, + route.metric); if(env->ExceptionCheck() || routeObj == NULL) { + LOGE("Exception creating VirtualNetworkRoute"); return NULL; } - jfieldID targetField = NULL; - jfieldID viaField = NULL; - jfieldID flagsField = NULL; - jfieldID metricField = NULL; - - targetField = lookup.findField(virtualNetworkRouteClass, "target", - "Ljava/net/InetSocketAddress;"); - if(env->ExceptionCheck() || targetField == NULL) - { - return NULL; - } - - viaField = lookup.findField(virtualNetworkRouteClass, "via", - "Ljava/net/InetSocketAddress;"); - if(env->ExceptionCheck() || targetField == NULL) - { - return NULL; - } - - flagsField = lookup.findField(virtualNetworkRouteClass, "flags", "I"); - if(env->ExceptionCheck() || flagsField == NULL) - { - return NULL; - } - - metricField = lookup.findField(virtualNetworkRouteClass, "metric", "I"); - if(env->ExceptionCheck() || metricField == NULL) - { - return NULL; - } - - jobject targetObj = newInetSocketAddress(env, route.target); - jobject viaObj = newInetSocketAddress(env, route.via); - - env->SetObjectField(routeObj, targetField, targetObj); - env->SetObjectField(routeObj, viaField, viaObj); - env->SetIntField(routeObj, flagsField, (jint)route.flags); - env->SetIntField(routeObj, metricField, (jint)route.metric); - return routeObj; } +// +// may return NULL +// jobject newVirtualNetworkDNS(JNIEnv *env, const ZT_VirtualNetworkDNS &dns) { - jclass virtualNetworkDNSClass = NULL; - jmethodID dnsConstructor = NULL; - - virtualNetworkDNSClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkDNS"); - if (env->ExceptionCheck() || virtualNetworkDNSClass == NULL) { + if (strlen(dns.domain) == 0) { + LOGD("dns.domain is empty; returning NULL"); return NULL; } - dnsConstructor = lookup.findMethod(virtualNetworkDNSClass, "", "()V"); - if(env->ExceptionCheck() || dnsConstructor == NULL) { + jstring domain = env->NewStringUTF(dns.domain); + if (env->ExceptionCheck() || domain == NULL) { + LOGE("Exception creating new string"); return NULL; } - jobject dnsObj = env->NewObject(virtualNetworkDNSClass, dnsConstructor); - if(env->ExceptionCheck() || dnsObj == NULL) { + jobject addrList = env->NewObject(ArrayList_class, ArrayList_ctor, 0); + if (env->ExceptionCheck() || addrList == NULL) { + LOGE("Exception creating new ArrayList"); return NULL; } - jfieldID domainField = NULL; - jfieldID serversField = NULL; + for (int i = 0; i < ZT_MAX_DNS_SERVERS; ++i) { //NOLINT - domainField = lookup.findField(virtualNetworkDNSClass, "domain", "Ljava/lang/String;"); - if(env->ExceptionCheck() || domainField == NULL) - { - return NULL; - } + struct sockaddr_storage tmp = dns.server_addr[i]; - serversField = lookup.findField(virtualNetworkDNSClass, "servers", "Ljava/util/ArrayList;"); - if(env->ExceptionCheck() || serversField == NULL) { - return NULL; - } - - if (strlen(dns.domain) > 0) { - InitListJNI(env); - jstring domain = env->NewStringUTF(dns.domain); - - jobject addrArray = env->NewObject(java_util_ArrayList, java_util_ArrayList_, 0); - - struct sockaddr_storage nullAddr; - memset(&nullAddr, 0, sizeof(struct sockaddr_storage)); - for(int i = 0; i < ZT_MAX_DNS_SERVERS; ++i) { - struct sockaddr_storage tmp = dns.server_addr[i]; - - if (memcmp(&tmp, &nullAddr, sizeof(struct sockaddr_storage)) != 0) { - jobject addr = newInetSocketAddress(env, tmp); - env->CallBooleanMethod(addrArray, java_util_ArrayList_add, addr); - env->DeleteLocalRef(addr); - } + // + // may be NULL + // + jobject addr = newInetSocketAddress(env, tmp); + if (env->ExceptionCheck()) { + return NULL; } - env->SetObjectField(dnsObj, domainField, domain); - env->SetObjectField(dnsObj, serversField, addrArray); + if (addr == NULL) { + continue; + } - return dnsObj; + env->CallBooleanMethod(addrList, ArrayList_add_method, addr); + if(env->ExceptionCheck()) + { + LOGE("Exception calling add"); + return NULL; + } + + env->DeleteLocalRef(addr); } - return NULL; + + jobject dnsObj = env->NewObject( + VirtualNetworkDNS_class, + VirtualNetworkDNS_ctor, + domain, + addrList); + if (env->ExceptionCheck() || dnsObj == NULL) { + LOGE("Exception creating new VirtualNetworkDNS"); + return NULL; + } + return dnsObj; } -#ifdef __cplusplus +jobject newNodeStatus(JNIEnv *env, const ZT_NodeStatus &status) { + + jstring pubIdentStr = env->NewStringUTF(status.publicIdentity); + if(env->ExceptionCheck() || pubIdentStr == NULL) + { + LOGE("Exception creating new string"); + return NULL; + } + + jstring secIdentStr = env->NewStringUTF(status.secretIdentity); + if(env->ExceptionCheck() || secIdentStr == NULL) + { + LOGE("Exception creating new string"); + return NULL; + } + + jobject nodeStatusObj = env->NewObject( + NodeStatus_class, + NodeStatus_ctor, + status.address, + pubIdentStr, + secIdentStr, + status.online); + if(env->ExceptionCheck() || nodeStatusObj == NULL) { + LOGE("Exception creating new NodeStatus"); + return NULL; + } + + return nodeStatusObj; +} + +jobjectArray newPeerArray(JNIEnv *env, const ZT_Peer *peers, size_t count) { + return newArrayObject(env, peers, count, Peer_class); +} + +jobjectArray newVirtualNetworkConfigArray(JNIEnv *env, const ZT_VirtualNetworkConfig *networks, size_t count) { + return newArrayObject(env, networks, count, VirtualNetworkConfig_class); +} + +jobjectArray newPeerPhysicalPathArray(JNIEnv *env, const ZT_PeerPhysicalPath *paths, size_t count) { + return newArrayObject(env, paths, count, PeerPhysicalPath_class); +} + +jobjectArray newInetSocketAddressArray(JNIEnv *env, const sockaddr_storage *addresses, size_t count) { + return newArrayObject(env, addresses, count, InetSocketAddress_class); +} + +jobjectArray newVirtualNetworkRouteArray(JNIEnv *env, const ZT_VirtualNetworkRoute *routes, size_t count) { + return newArrayObject(env, routes, count, VirtualNetworkRoute_class); +} + +void newArrayObject_logCount(size_t count) { + LOGE("count > JSIZE_MAX: %zu", count); +} + +void newArrayObject_log(const char *msg) { + LOGE("%s", msg); +} + +jbyteArray newByteArray(JNIEnv *env, const unsigned char *bytes, size_t count) { + + if (count > JSIZE_MAX) { + LOGE("count > JSIZE_MAX: %zu", count); + return NULL; + } + + jsize jCount = static_cast(count); + const jbyte *jBytes = reinterpret_cast(bytes); + + jbyteArray byteArrayObj = env->NewByteArray(jCount); + if(byteArrayObj == NULL) + { + LOGE("NewByteArray returned NULL"); + return NULL; + } + + env->SetByteArrayRegion(byteArrayObj, 0, jCount, jBytes); + if (env->ExceptionCheck()) { + LOGE("Exception when calling SetByteArrayRegion"); + return NULL; + } + + return byteArrayObj; +} + +jbyteArray newByteArray(JNIEnv *env, size_t count) { + + if (count > JSIZE_MAX) { + LOGE("count > JSIZE_MAX: %zu", count); + return NULL; + } + + jsize jCount = static_cast(count); + + jbyteArray byteArrayObj = env->NewByteArray(jCount); + if(byteArrayObj == NULL) + { + LOGE("NewByteArray returned NULL"); + return NULL; + } + + return byteArrayObj; +} + +bool isSocketAddressEmpty(const sockaddr_storage addr) { + + // + // was: + // struct sockaddr_storage nullAddress = {0}; + // + // but was getting this warning: + // warning: suggest braces around initialization of subobject + // + // when building ZeroTierOne + // + sockaddr_storage emptyAddress; //NOLINT + + // + // It is possible to assume knowledge about internals of sockaddr_storage and construct + // correct 0-initializer, but it is simpler to just treat sockaddr_storage as opaque and + // use memset here to fill with 0 + // + // This is also done in InetAddress.hpp for InetAddress + // + memset(&emptyAddress, 0, sizeof(sockaddr_storage)); + + return (memcmp(&addr, &emptyAddress, sizeof(sockaddr_storage)) == 0); //NOLINT +} + +// +// returns empty sockaddr_storage on error +// +sockaddr_storage fromSocketAddressObject(JNIEnv *env, jobject sockAddressObject) { + + sockaddr_storage emptyAddress; //NOLINT + + memset(&emptyAddress, 0, sizeof(sockaddr_storage)); + + jint port = env->CallIntMethod(sockAddressObject, InetSocketAddress_getPort_method); + if(env->ExceptionCheck()) + { + LOGE("Exception calling getPort"); + return emptyAddress; + } + + jobject addressObject = env->CallObjectMethod(sockAddressObject, InetSocketAddress_getAddress_method); + if(env->ExceptionCheck() || addressObject == NULL) + { + LOGE("Exception calling getAddress"); + return emptyAddress; + } + + jbyteArray addressArrayObj = reinterpret_cast(env->CallObjectMethod(addressObject, InetAddress_getAddress_method)); + if(env->ExceptionCheck() || addressArrayObj == NULL) + { + LOGE("Exception calling getAddress"); + return emptyAddress; + } + + sockaddr_storage addr = {}; + + if (env->IsInstanceOf(addressObject, Inet4Address_class)) { + + // IPV4 + + assert(env->GetArrayLength(addressArrayObj) == 4); + + sockaddr_in *addr_4 = reinterpret_cast(&addr); + addr_4->sin_family = AF_INET; + addr_4->sin_port = htons(port); + + void *data = env->GetPrimitiveArrayCritical(addressArrayObj, NULL); + memcpy(&addr_4->sin_addr.s_addr, data, 4); + env->ReleasePrimitiveArrayCritical(addressArrayObj, data, 0); + + } else if (env->IsInstanceOf(addressObject, Inet6Address_class)) { + + // IPV6 + + assert(env->GetArrayLength(addressArrayObj) == 16); + + sockaddr_in6 *addr_6 = reinterpret_cast(&addr); + addr_6->sin6_family = AF_INET6; + addr_6->sin6_port = htons(port); + + void *data = env->GetPrimitiveArrayCritical(addressArrayObj, NULL); + memcpy(&addr_6->sin6_addr.s6_addr, data, 16); + env->ReleasePrimitiveArrayCritical(addressArrayObj, data, 0); + + } else { + assert(false && "addressObject is neither Inet4Address nor Inet6Address"); + } + + return addr; } -#endif diff --git a/java/jni/ZT_jniutils.h b/java/jni/ZT_jniutils.h index 3e81b934d..23664a732 100644 --- a/java/jni/ZT_jniutils.h +++ b/java/jni/ZT_jniutils.h @@ -15,18 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - + #ifndef ZT_jniutils_h_ #define ZT_jniutils_h_ -#include + #include #include -#ifdef __cplusplus -extern "C" { -#endif - -#define LOG_TAG "ZeroTierOneJNI" +#include // for numeric_limits +#include // for sockaddr_storage #if defined(__ANDROID__) @@ -55,6 +52,34 @@ extern "C" { #define LOGE(...) fprintf(stdout, __VA_ARGS__) #endif +// +// Call GetEnv and assert if there is an error +// +#define GETENV(env, vm) \ + do { \ + jint getEnvRet; \ + assert(vm); \ + if ((getEnvRet = vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6)) != JNI_OK) { \ + LOGE("Error calling GetEnv: %d", getEnvRet); \ + assert(false && "Error calling GetEnv"); \ + } \ + } while (false) + +// +// Call GetJavaVM and assert if there is an error +// +#define GETJAVAVM(env, vm) \ + do { \ + jint getJavaVMRet; \ + if ((getJavaVMRet = env->GetJavaVM(&vm)) != 0) { \ + LOGE("Error calling GetJavaVM: %d", getJavaVMRet); \ + assert(false && "Error calling GetJavaVM"); \ + } \ + } while (false) + + +const jsize JSIZE_MAX = std::numeric_limits::max(); + jobject createResultObject(JNIEnv *env, ZT_ResultCode code); jobject createVirtualNetworkStatus(JNIEnv *env, ZT_VirtualNetworkStatus status); jobject createVirtualNetworkType(JNIEnv *env, ZT_VirtualNetworkType type); @@ -64,8 +89,7 @@ jobject createVirtualNetworkConfigOperation(JNIEnv *env, ZT_VirtualNetworkConfig jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr); jobject newInetAddress(JNIEnv *env, const sockaddr_storage &addr); - -jobject newMulticastGroup(JNIEnv *env, const ZT_MulticastGroup &mc); +int addressPort(const sockaddr_storage addr); jobject newPeer(JNIEnv *env, const ZT_Peer &peer); jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp); @@ -78,8 +102,68 @@ jobject newVirtualNetworkRoute(JNIEnv *env, const ZT_VirtualNetworkRoute &route) jobject newVirtualNetworkDNS(JNIEnv *env, const ZT_VirtualNetworkDNS &dns); -#ifdef __cplusplus -} -#endif +jobject newNodeStatus(JNIEnv *env, const ZT_NodeStatus &status); -#endif \ No newline at end of file +jobjectArray newPeerArray(JNIEnv *env, const ZT_Peer *peers, size_t count); + +jobjectArray newVirtualNetworkConfigArray(JNIEnv *env, const ZT_VirtualNetworkConfig *networks, size_t count); + +jobjectArray newPeerPhysicalPathArray(JNIEnv *env, const ZT_PeerPhysicalPath *paths, size_t count); + +jobjectArray newInetSocketAddressArray(JNIEnv *env, const sockaddr_storage *addresses, size_t count); + +jobjectArray newVirtualNetworkRouteArray(JNIEnv *env, const ZT_VirtualNetworkRoute *routes, size_t count); + +// +// log functions only for newArrayObject below +// +void newArrayObject_logCount(size_t count); +void newArrayObject_log(const char *msg); + +// +// function template for creating array objects +// +template +jobjectArray newArrayObject(JNIEnv *env, const T *buffer, size_t count, jclass clazz) { + + if (count > JSIZE_MAX) { + newArrayObject_logCount(count); + return NULL; + } + + jsize jCount = static_cast(count); + + jobjectArray arrayObj = env->NewObjectArray(jCount, clazz, NULL); + if (env->ExceptionCheck() || arrayObj == NULL) { + newArrayObject_log("Error creating array object"); + return NULL; + } + + for (jsize i = 0; i < jCount; i++) { + + jobject obj = F(env, buffer[i]); + if(env->ExceptionCheck() || obj == NULL) { + return NULL; + } + + env->SetObjectArrayElement(arrayObj, i, obj); + if(env->ExceptionCheck()) { + newArrayObject_log("Error assigning object to array"); + return NULL; + } + + env->DeleteLocalRef(obj); + } + + return arrayObj; +} + +jbyteArray newByteArray(JNIEnv *env, const unsigned char *bytes, size_t count); + +jbyteArray newByteArray(JNIEnv *env, size_t count); + +bool isSocketAddressEmpty(const sockaddr_storage addr); + +sockaddr_storage fromSocketAddressObject(JNIEnv *env, jobject sockAddressObject); + +#endif // ZT_jniutils_h_ diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index 75af18bc8..536c7cb6a 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -26,47 +26,62 @@ */ #include "com_zerotierone_sdk_Node.h" + +#include "ZT_jnicache.h" #include "ZT_jniutils.h" -#include "ZT_jnilookup.h" #include #include "Mutex.hpp" #include #include -#include -#include +#include +#include +#include // for PRId64 -// global static JNI Lookup Object -JniLookup lookup; - -#ifdef __cplusplus -extern "C" { -#endif +#define LOG_TAG "Node" namespace { struct JniRef { - JniRef() - : jvm(NULL) - , node(NULL) - , dataStoreGetListener(NULL) - , dataStorePutListener(NULL) - , packetSender(NULL) - , eventListener(NULL) - , frameListener(NULL) - , configListener(NULL) - , pathChecker(NULL) - , callbacks(NULL) - { - callbacks = (ZT_Node_Callbacks*)malloc(sizeof(ZT_Node_Callbacks)); - memset(callbacks, 0, sizeof(ZT_Node_Callbacks)); - } + JniRef( + int64_t id, + JavaVM *jvm, + jobject dataStoreGetListenerLocalIn, + jobject dataStorePutListenerLocalIn, + jobject packetSenderLocalIn, + jobject eventListenerLocalIn, + jobject frameListenerLocalIn, + jobject configListenerLocalIn, + jobject pathCheckerLocalIn) + : id(id) + , jvm(jvm) + , node() + , dataStoreGetListener() + , dataStorePutListener() + , packetSender() + , eventListener() + , frameListener() + , configListener() + , pathChecker() + , inited() { + + JNIEnv *env; + GETENV(env, jvm); + + dataStoreGetListener = env->NewGlobalRef(dataStoreGetListenerLocalIn); + dataStorePutListener = env->NewGlobalRef(dataStorePutListenerLocalIn); + packetSender = env->NewGlobalRef(packetSenderLocalIn); + eventListener = env->NewGlobalRef(eventListenerLocalIn); + frameListener = env->NewGlobalRef(frameListenerLocalIn); + configListener = env->NewGlobalRef(configListenerLocalIn); + pathChecker = env->NewGlobalRef(pathCheckerLocalIn); + }; ~JniRef() { - JNIEnv *env = NULL; - jvm->GetEnv((void**)&env, JNI_VERSION_1_6); + JNIEnv *env; + GETENV(env, jvm); env->DeleteGlobalRef(dataStoreGetListener); env->DeleteGlobalRef(dataStorePutListener); @@ -75,9 +90,6 @@ namespace { env->DeleteGlobalRef(frameListener); env->DeleteGlobalRef(configListener); env->DeleteGlobalRef(pathChecker); - - free(callbacks); - callbacks = NULL; } int64_t id; @@ -94,70 +106,76 @@ namespace { jobject configListener; jobject pathChecker; - ZT_Node_Callbacks *callbacks; + bool inited; + + bool finishInitializing(); }; + /* + * 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. + */ int VirtualNetworkConfigFunctionCallback( ZT_Node *node, void *userData, void *threadData, uint64_t nwid, - void **, + void **nuptr, enum ZT_VirtualNetworkConfigOperation operation, const ZT_VirtualNetworkConfig *config) { LOGV("VirtualNetworkConfigFunctionCallback"); JniRef *ref = (JniRef*)userData; - JNIEnv *env = NULL; - ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6); + assert(ref); + JNIEnv *env; + GETENV(env, ref->jvm); + + if (env->ExceptionCheck()) { + LOGE("Unhandled pending exception"); + return -100; + } if (ref->configListener == NULL) { LOGE("configListener is NULL"); - return -1; - } - - jclass configListenerClass = env->GetObjectClass(ref->configListener); - if(configListenerClass == NULL) - { - LOGE("Couldn't find class for VirtualNetworkConfigListener instance"); - return -1; - } - - jmethodID configListenerCallbackMethod = lookup.findMethod(configListenerClass, - "onNetworkConfigurationUpdated", - "(JLcom/zerotier/sdk/VirtualNetworkConfigOperation;Lcom/zerotier/sdk/VirtualNetworkConfig;)I"); - if(configListenerCallbackMethod == NULL) - { - LOGE("Couldn't find onVirtualNetworkFrame() method"); - return -2; + return -101; } jobject operationObject = createVirtualNetworkConfigOperation(env, operation); - if(operationObject == NULL) + if(env->ExceptionCheck() || operationObject == NULL) { - LOGE("Error creating VirtualNetworkConfigOperation object"); - return -3; + return -102; + } + + if (config == NULL) { + LOGE("Config is NULL"); + return -103; } jobject networkConfigObject = newNetworkConfig(env, *config); - if(networkConfigObject == NULL) + if(env->ExceptionCheck() || networkConfigObject == NULL) { - LOGE("Error creating VirtualNetworkConfig object"); - return -4; + return -104; } - return env->CallIntMethod( + jint ret = env->CallIntMethod( ref->configListener, - configListenerCallbackMethod, + VirtualNetworkConfigListener_onNetworkConfigurationUpdated_method, (jlong)nwid, operationObject, networkConfigObject); + if (env->ExceptionCheck()) { + LOGE("Exception calling onNetworkConfigurationUpdated"); + return -105; + } + + return ret; } void VirtualNetworkFrameFunctionCallback(ZT_Node *node, void *userData, void *threadData, uint64_t nwid, - void**, + void** nuptr, uint64_t sourceMac, uint64_t destMac, unsigned int etherType, @@ -167,53 +185,39 @@ namespace { { LOGV("VirtualNetworkFrameFunctionCallback"); #ifndef NDEBUG - unsigned char* local = (unsigned char*)frameData; - LOGV("Type Bytes: 0x%02x%02x", local[12], local[13]); + if (frameLength >= 14) { + unsigned char* local = (unsigned char*)frameData; + LOGV("Type Bytes: 0x%02x%02x", local[12], local[13]); + } #endif JniRef *ref = (JniRef*)userData; + assert(ref); assert(ref->node == node); - JNIEnv *env = NULL; - ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6); + JNIEnv *env; + GETENV(env, ref->jvm); + + if (env->ExceptionCheck()) { + LOGE("Unhandled pending exception"); + return; + } if (ref->frameListener == NULL) { LOGE("frameListener is NULL"); return; } - jclass frameListenerClass = env->GetObjectClass(ref->frameListener); - if(env->ExceptionCheck() || frameListenerClass == NULL) - { - LOGE("Couldn't find class for VirtualNetworkFrameListener instance"); - return; - } - - jmethodID frameListenerCallbackMethod = lookup.findMethod( - frameListenerClass, - "onVirtualNetworkFrame", "(JJJJJ[B)V"); - if(env->ExceptionCheck() || frameListenerCallbackMethod == NULL) - { - LOGE("Couldn't find onVirtualNetworkFrame() method"); - return; - } - - jbyteArray dataArray = env->NewByteArray(frameLength); + const unsigned char *bytes = static_cast(frameData); + jbyteArray dataArray = newByteArray(env, bytes, frameLength); if(env->ExceptionCheck() || dataArray == NULL) { - LOGE("Couldn't create frame data array"); return; } - void *data = env->GetPrimitiveArrayCritical(dataArray, NULL); - memcpy(data, frameData, frameLength); - env->ReleasePrimitiveArrayCritical(dataArray, data, 0); - - if(env->ExceptionCheck()) - { - LOGE("Error setting frame data to array"); + env->CallVoidMethod(ref->frameListener, VirtualNetworkFrameListener_onVirtualNetworkFrame_method, (jlong)nwid, (jlong)sourceMac, (jlong)destMac, (jlong)etherType, (jlong)vlanid, dataArray); + if (env->ExceptionCheck()) { + LOGE("Exception calling onVirtualNetworkFrame"); return; } - - env->CallVoidMethod(ref->frameListener, frameListenerCallbackMethod, (jlong)nwid, (jlong)sourceMac, (jlong)destMac, (jlong)etherType, (jlong)vlanid, dataArray); } @@ -224,83 +228,98 @@ namespace { const void *data) { LOGV("EventCallback"); JniRef *ref = (JniRef *) userData; + assert(ref); if (ref->node != node && event != ZT_EVENT_UP) { LOGE("Nodes not equal. ref->node %p, node %p. Event: %d", ref->node, node, event); return; } - JNIEnv *env = NULL; - ref->jvm->GetEnv((void **) &env, JNI_VERSION_1_6); + JNIEnv *env; + GETENV(env, ref->jvm); + + if (env->ExceptionCheck()) { + LOGE("Unhandled pending exception"); + return; + } if (ref->eventListener == NULL) { LOGE("eventListener is NULL"); return; } - jclass eventListenerClass = env->GetObjectClass(ref->eventListener); - if (eventListenerClass == NULL) { - LOGE("Couldn't class for EventListener instance"); - return; - } - - jmethodID onEventMethod = lookup.findMethod(eventListenerClass, - "onEvent", "(Lcom/zerotier/sdk/Event;)V"); - if (onEventMethod == NULL) { - LOGE("Couldn't find onEvent method"); - return; - } - - jmethodID onTraceMethod = lookup.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) { + if (env->ExceptionCheck() || eventObject == NULL) { return; } switch (event) { case ZT_EVENT_UP: { LOGD("Event Up"); - env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject); + env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, eventObject); + if (env->ExceptionCheck()) { + LOGE("Exception calling onEvent"); + return; + } break; } case ZT_EVENT_OFFLINE: { LOGD("Event Offline"); - env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject); + env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, eventObject); + if (env->ExceptionCheck()) { + LOGE("Exception calling onEvent"); + return; + } break; } case ZT_EVENT_ONLINE: { LOGD("Event Online"); - env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject); + env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, eventObject); + if (env->ExceptionCheck()) { + LOGE("Exception calling onEvent"); + return; + } break; } case ZT_EVENT_DOWN: { LOGD("Event Down"); - env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject); + env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, eventObject); + if (env->ExceptionCheck()) { + LOGE("Exception calling onEvent"); + return; + } break; } case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION: { LOGV("Identity Collision"); // call onEvent() - env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject); + env->CallVoidMethod(ref->eventListener, EventListener_onEvent_method, eventObject); + if (env->ExceptionCheck()) { + LOGE("Exception calling onEvent"); + return; + } } break; case ZT_EVENT_TRACE: { LOGV("Trace Event"); // call onTrace() - if (data != NULL) { - const char *message = (const char *) data; - jstring messageStr = env->NewStringUTF(message); - env->CallVoidMethod(ref->eventListener, onTraceMethod, messageStr); + if (data == NULL) { + break; + } + const char *message = (const char *) data; + jstring messageStr = env->NewStringUTF(message); + if (env->ExceptionCheck() || messageStr == NULL) { + LOGE("Exception creating new string"); + return; + } + + env->CallVoidMethod(ref->eventListener, EventListener_onTrace_method, messageStr); + if (env->ExceptionCheck()) { + LOGE("Exception calling onTrace"); + return; } } break; case ZT_EVENT_USER_MESSAGE: case ZT_EVENT_REMOTE_TRACE: - default: break; } } @@ -313,93 +332,101 @@ namespace { const uint64_t id[2], const void *buffer, int bufferLength) { + LOGV("StatePutFunction"); + char p[4096] = {0}; bool secure = false; + int res = 0; switch (type) { case ZT_STATE_OBJECT_IDENTITY_PUBLIC: - snprintf(p, sizeof(p), "identity.public"); + res = snprintf(p, sizeof(p), "identity.public"); break; case ZT_STATE_OBJECT_IDENTITY_SECRET: - snprintf(p, sizeof(p), "identity.secret"); + res = snprintf(p, sizeof(p), "identity.secret"); secure = true; break; case ZT_STATE_OBJECT_PLANET: - snprintf(p, sizeof(p), "planet"); + res = snprintf(p, sizeof(p), "planet"); break; case ZT_STATE_OBJECT_MOON: - snprintf(p, sizeof(p), "moons.d/%.16llx.moon", (unsigned long long)id[0]); + res = snprintf(p, sizeof(p), "moons.d/%.16" PRIx64 ".moon", id[0]); break; case ZT_STATE_OBJECT_NETWORK_CONFIG: - snprintf(p, sizeof(p), "networks.d/%.16llx.conf", (unsigned long long)id[0]); + res = snprintf(p, sizeof(p), "networks.d/%.16" PRIx64 ".conf", id[0]); break; case ZT_STATE_OBJECT_PEER: - snprintf(p, sizeof(p), "peers.d/%.10llx", (unsigned long long)id[0]); + res = snprintf(p, sizeof(p), "peers.d/%.10" PRIx64, id[0]); break; - default: + case ZT_STATE_OBJECT_NULL: return; } - if (strlen(p) < 1) { + if (!(0 <= res && res < sizeof(p))) { + LOGE("snprintf error: %d", res); return; } JniRef *ref = (JniRef*)userData; - JNIEnv *env = NULL; - ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6); + assert(ref); + JNIEnv *env; + GETENV(env, ref->jvm); + + if (env->ExceptionCheck()) { + LOGE("Unhandled pending exception"); + return; + } if (ref->dataStorePutListener == NULL) { LOGE("dataStorePutListener is NULL"); return; } - jclass dataStorePutClass = env->GetObjectClass(ref->dataStorePutListener); - if (dataStorePutClass == NULL) - { - LOGE("Couldn't find class for DataStorePutListener instance"); - return; - } - - jmethodID dataStorePutCallbackMethod = lookup.findMethod( - dataStorePutClass, - "onDataStorePut", - "(Ljava/lang/String;[BZ)I"); - if(dataStorePutCallbackMethod == NULL) - { - LOGE("Couldn't find onDataStorePut method"); - return; - } - - jmethodID deleteMethod = lookup.findMethod(dataStorePutClass, - "onDelete", "(Ljava/lang/String;)I"); - if(deleteMethod == NULL) - { - LOGE("Couldn't find onDelete method"); - return; - } - jstring nameStr = env->NewStringUTF(p); + if (env->ExceptionCheck() || nameStr == NULL) { + LOGE("Exception creating new string"); + return; + } if (bufferLength >= 0) { LOGD("JNI: Write file: %s", p); - // set operation - jbyteArray bufferObj = env->NewByteArray(bufferLength); + const unsigned char *bytes = static_cast(buffer); + jbyteArray bufferObj = newByteArray(env, bytes, bufferLength); if(env->ExceptionCheck() || bufferObj == NULL) { - LOGE("Error creating byte array buffer!"); return; } - env->SetByteArrayRegion(bufferObj, 0, bufferLength, (jbyte*)buffer); - - env->CallIntMethod(ref->dataStorePutListener, - dataStorePutCallbackMethod, + int retval = env->CallIntMethod(ref->dataStorePutListener, + DataStorePutListener_onDataStorePut_method, nameStr, bufferObj, secure); + if (env->ExceptionCheck()) { + LOGE("Exception calling onDataStorePut"); + return; + } + + if (retval != 0) { + LOGE("onDataStorePut error: %d", retval); + } + } else { LOGD("JNI: Delete file: %s", p); - env->CallIntMethod(ref->dataStorePutListener, deleteMethod, nameStr); + int retval = env->CallIntMethod(ref->dataStorePutListener, DataStorePutListener_onDelete_method, nameStr); + if (env->ExceptionCheck()) { + LOGE("Exception calling onDelete"); + return; + } + + if (retval != 0) { + LOGE("onDelete error: %d", retval); + } } } + /** + * This function should return the number of bytes actually stored to the + * buffer or -1 if the state object was not found or the buffer was too + * small to store it. + */ int StateGetFunction( ZT_Node *node, void *userData, @@ -408,86 +435,87 @@ namespace { const uint64_t id[2], void *buffer, unsigned int bufferLength) { + LOGV("StateGetFunction"); + char p[4096] = {0}; + int res = 0; switch (type) { case ZT_STATE_OBJECT_IDENTITY_PUBLIC: - snprintf(p, sizeof(p), "identity.public"); + res = snprintf(p, sizeof(p), "identity.public"); break; case ZT_STATE_OBJECT_IDENTITY_SECRET: - snprintf(p, sizeof(p), "identity.secret"); + res = snprintf(p, sizeof(p), "identity.secret"); break; case ZT_STATE_OBJECT_PLANET: - snprintf(p, sizeof(p), "planet"); + res = snprintf(p, sizeof(p), "planet"); break; case ZT_STATE_OBJECT_MOON: - snprintf(p, sizeof(p), "moons.d/%.16llx.moon", (unsigned long long)id[0]); + res = snprintf(p, sizeof(p), "moons.d/%.16" PRIx64 ".moon", id[0]); break; case ZT_STATE_OBJECT_NETWORK_CONFIG: - snprintf(p, sizeof(p), "networks.d/%.16llx.conf", (unsigned long long)id[0]); + res = snprintf(p, sizeof(p), "networks.d/%.16" PRIx64 ".conf", id[0]); break; case ZT_STATE_OBJECT_PEER: - snprintf(p, sizeof(p), "peers.d/%.10llx", (unsigned long long)id[0]); + res = snprintf(p, sizeof(p), "peers.d/%.10" PRIx64, id[0]); break; - default: - return -1; + case ZT_STATE_OBJECT_NULL: + return -100; } - if (strlen(p) < 1) { - return -1; + if (!(0 <= res && res < sizeof(p))) { + LOGE("snprintf error: %d", res); + return -101; } JniRef *ref = (JniRef*)userData; - JNIEnv *env = NULL; - ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6); + assert(ref); + JNIEnv *env; + GETENV(env, ref->jvm); + + if (env->ExceptionCheck()) { + LOGE("Unhandled pending exception"); + return -102; + } if (ref->dataStoreGetListener == NULL) { LOGE("dataStoreGetListener is NULL"); - return -2; - } - - jclass dataStoreGetClass = env->GetObjectClass(ref->dataStoreGetListener); - if(dataStoreGetClass == NULL) - { - LOGE("Couldn't find class for DataStoreGetListener instance"); - return -2; - } - - jmethodID dataStoreGetCallbackMethod = lookup.findMethod( - dataStoreGetClass, - "onDataStoreGet", - "(Ljava/lang/String;[B)J"); - if(dataStoreGetCallbackMethod == NULL) - { - LOGE("Couldn't find onDataStoreGet method"); - return -2; + return -103; } jstring nameStr = env->NewStringUTF(p); - if(nameStr == NULL) + if(env->ExceptionCheck() || nameStr == NULL) { LOGE("Error creating name string object"); - return -2; // out of memory + return -104; // out of memory } - jbyteArray bufferObj = env->NewByteArray(bufferLength); - if(bufferObj == NULL) + jbyteArray bufferObj = newByteArray(env, bufferLength); + if(env->ExceptionCheck() || bufferObj == NULL) { - LOGE("Error creating byte[] buffer of size: %u", bufferLength); - return -2; + return -105; } LOGV("Calling onDataStoreGet(%s, %p)", p, buffer); int retval = (int)env->CallLongMethod( ref->dataStoreGetListener, - dataStoreGetCallbackMethod, + DataStoreGetListener_onDataStoreGet_method, nameStr, bufferObj); + if (env->ExceptionCheck()) { + LOGE("Exception calling onDataStoreGet"); + return -106; + } LOGV("onDataStoreGet returned %d", retval); if(retval > 0) { + if (retval > bufferLength) { + LOGE("retval > bufferLength. retval: %d, bufferLength: %u", retval, bufferLength); + return -107; + } + void *data = env->GetPrimitiveArrayCritical(bufferObj, NULL); memcpy(buffer, data, retval); env->ReleasePrimitiveArrayCritical(bufferObj, data, 0); @@ -496,6 +524,11 @@ namespace { return retval; } + /** + * 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. + */ int WirePacketSendFunction(ZT_Node *node, void *userData, void *threadData, @@ -505,42 +538,51 @@ namespace { unsigned int bufferSize, unsigned int ttl) { - LOGV("WirePacketSendFunction(%lld, %p, %p, %d)", (long long)localSocket, remoteAddress, buffer, bufferSize); + LOGV("WirePacketSendFunction(%" PRId64 ", %p, %p, %u, %u)", localSocket, remoteAddress, buffer, bufferSize, ttl); JniRef *ref = (JniRef*)userData; + assert(ref); assert(ref->node == node); - JNIEnv *env = NULL; - ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6); + JNIEnv *env; + GETENV(env, ref->jvm); + + if (env->ExceptionCheck()) { + LOGE("Unhandled pending exception"); + return -100; + } if (ref->packetSender == NULL) { LOGE("packetSender is NULL"); - return -1; - } - - jclass packetSenderClass = env->GetObjectClass(ref->packetSender); - if(packetSenderClass == NULL) - { - LOGE("Couldn't find class for PacketSender instance"); - return -1; - } - - jmethodID packetSenderCallbackMethod = lookup.findMethod(packetSenderClass, - "onSendPacketRequested", "(JLjava/net/InetSocketAddress;[BI)I"); - if(packetSenderCallbackMethod == NULL) - { - LOGE("Couldn't find onSendPacketRequested method"); - return -2; + return -101; } + // + // may be NULL + // jobject remoteAddressObj = newInetSocketAddress(env, *remoteAddress); - jbyteArray bufferObj = env->NewByteArray(bufferSize); - env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer); - int retval = env->CallIntMethod(ref->packetSender, packetSenderCallbackMethod, localSocket, remoteAddressObj, bufferObj); + if (env->ExceptionCheck()) { + return -102; + } + const unsigned char *bytes = static_cast(buffer); + jbyteArray bufferObj = newByteArray(env, bytes, bufferSize); + if (env->ExceptionCheck() || bufferObj == NULL) + { + return -103; + } + + int retval = env->CallIntMethod(ref->packetSender, PacketSender_onSendPacketRequested_method, localSocket, remoteAddressObj, bufferObj, 0); + if (env->ExceptionCheck()) { + LOGE("Exception calling onSendPacketRequested"); + return -104; + } LOGV("JNI Packet Sender returned: %d", retval); return retval; } + /** + * This function must return nonzero (true) if the path should be used. + */ int PathCheckFunction(ZT_Node *node, void *userPtr, void *threadPtr, @@ -548,61 +590,44 @@ namespace { int64_t localSocket, const struct sockaddr_storage *remoteAddress) { + LOGV("PathCheckFunction"); + JniRef *ref = (JniRef*)userPtr; + assert(ref); assert(ref->node == node); if(ref->pathChecker == NULL) { return true; } - JNIEnv *env = NULL; - ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6); + JNIEnv *env; + GETENV(env, ref->jvm); - jclass pathCheckerClass = env->GetObjectClass(ref->pathChecker); - if(pathCheckerClass == NULL) - { - LOGE("Couldn't find class for PathChecker instance"); - return true; - } - - jmethodID pathCheckCallbackMethod = lookup.findMethod(pathCheckerClass, - "onPathCheck", "(JJLjava/net/InetSocketAddress;)Z"); - if(pathCheckCallbackMethod == NULL) - { - LOGE("Couldn't find onPathCheck method implementation"); + if (env->ExceptionCheck()) { + LOGE("Unhandled pending exception"); return true; } // - // was: - // struct sockaddr_storage nullAddress = {0}; + // may be NULL // - // but was getting this warning: - // warning: suggest braces around initialization of subobject - // - // when building ZeroTierOne - // - struct sockaddr_storage nullAddress; - - // - // It is possible to assume knowledge about internals of sockaddr_storage and construct - // correct 0-initializer, but it is simpler to just treat sockaddr_storage as opaque and - // use memset here to fill with 0 - // - // This is also done in InetAddress.hpp for InetAddress - // - memset(&nullAddress, 0, sizeof(sockaddr_storage)); - - jobject remoteAddressObj = NULL; - - if(memcmp(remoteAddress, &nullAddress, sizeof(sockaddr_storage)) != 0) - { - remoteAddressObj = newInetSocketAddress(env, *remoteAddress); + jobject remoteAddressObj = newInetSocketAddress(env, *remoteAddress); + if (env->ExceptionCheck()) { + return true; } - return env->CallBooleanMethod(ref->pathChecker, pathCheckCallbackMethod, address, localSocket, remoteAddressObj); + jboolean ret = env->CallBooleanMethod(ref->pathChecker, PathChecker_onPathCheck_method, address, localSocket, remoteAddressObj); + if (env->ExceptionCheck()) { + LOGE("Exception calling onPathCheck"); + return true; + } + + return ret; } + /** + * It must return a nonzero (true) value if the result buffer has been filled with an address. + */ int PathLookupFunction(ZT_Node *node, void *userPtr, void *threadPtr, @@ -610,105 +635,41 @@ namespace { int ss_family, struct sockaddr_storage *result) { + LOGV("PathLookupFunction"); + JniRef *ref = (JniRef*)userPtr; + assert(ref); assert(ref->node == node); if(ref->pathChecker == NULL) { return false; } - JNIEnv *env = NULL; - ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6); + JNIEnv *env; + GETENV(env, ref->jvm); - jclass pathCheckerClass = env->GetObjectClass(ref->pathChecker); - if(pathCheckerClass == NULL) - { - LOGE("Couldn't find class for PathChecker instance"); + if (env->ExceptionCheck()) { + LOGE("Unhandled pending exception"); + return false; + } + + // + // may be NULL + // + jobject sockAddressObject = env->CallObjectMethod(ref->pathChecker, PathChecker_onPathLookup_method, address, ss_family); + if (env->ExceptionCheck()) { + LOGE("Unable to call onPathLookup implementation"); return false; } - jmethodID pathLookupMethod = lookup.findMethod(pathCheckerClass, - "onPathLookup", "(JI)Ljava/net/InetSocketAddress;"); - if(pathLookupMethod == NULL) { - return false; - } - - jobject sockAddressObject = env->CallObjectMethod(ref->pathChecker, pathLookupMethod, address, ss_family); if(sockAddressObject == NULL) { LOGE("Unable to call onPathLookup implementation"); return false; } - jclass inetSockAddressClass = env->GetObjectClass(sockAddressObject); - if(inetSockAddressClass == NULL) - { - LOGE("Unable to find InetSocketAddress class"); - return false; - } - - jmethodID getAddressMethod = lookup.findMethod(inetSockAddressClass, "getAddress", "()Ljava/net/InetAddress;"); - if(getAddressMethod == NULL) - { - LOGE("Unable to find InetSocketAddress.getAddress() method"); - return false; - } - - jmethodID getPortMethod = lookup.findMethod(inetSockAddressClass, "getPort", "()I"); - if(getPortMethod == NULL) - { - LOGE("Unable to find InetSocketAddress.getPort() method"); - return false; - } - - jint port = env->CallIntMethod(sockAddressObject, getPortMethod); - jobject addressObject = env->CallObjectMethod(sockAddressObject, getAddressMethod); - - jclass inetAddressClass = lookup.findClass("java/net/InetAddress"); - if(inetAddressClass == NULL) - { - LOGE("Unable to find InetAddress class"); - return false; - } - - getAddressMethod = lookup.findMethod(inetAddressClass, "getAddress", "()[B"); - if(getAddressMethod == NULL) - { - LOGE("Unable to find InetAddress.getAddress() method"); - return false; - } - - jbyteArray addressBytes = (jbyteArray)env->CallObjectMethod(addressObject, getAddressMethod); - if(addressBytes == NULL) - { - LOGE("Unable to call InetAddress.getBytes()"); - return false; - } - - int addressSize = env->GetArrayLength(addressBytes); - if(addressSize == 4) - { - // IPV4 - sockaddr_in *addr = (sockaddr_in*)result; - addr->sin_family = AF_INET; - addr->sin_port = htons(port); - - void *data = env->GetPrimitiveArrayCritical(addressBytes, NULL); - memcpy(&addr->sin_addr, data, 4); - env->ReleasePrimitiveArrayCritical(addressBytes, data, 0); - } - else if (addressSize == 16) - { - // IPV6 - sockaddr_in6 *addr = (sockaddr_in6*)result; - addr->sin6_family = AF_INET6; - addr->sin6_port = htons(port); - void *data = env->GetPrimitiveArrayCritical(addressBytes, NULL); - memcpy(&addr->sin6_addr, data, 16); - env->ReleasePrimitiveArrayCritical(addressBytes, data, 0); - } - else - { + *result = fromSocketAddressObject(env, sockAddressObject); + if (env->ExceptionCheck() || isSocketAddressEmpty(*result)) { return false; } @@ -716,171 +677,179 @@ namespace { } typedef std::map NodeMap; - static NodeMap nodeMap; + NodeMap nodeMap; ZeroTier::Mutex nodeMapMutex; + bool isInited(int64_t nodeId) { + + ZeroTier::Mutex::Lock lock(nodeMapMutex); + NodeMap::iterator found = nodeMap.find(nodeId); + + if (found == nodeMap.end()) { + + // + // not in map yet, or has been removed from map + // + return false; + } + + JniRef *ref = found->second; + + assert(ref); + + return ref->inited; + } + + bool JniRef::finishInitializing() { + + ZeroTier::Mutex::Lock lock(nodeMapMutex); + NodeMap::iterator found = nodeMap.find(id); + + if (found != nodeMap.end()) { + // + // already in map + // + LOGE("Cannot finish initializing; node is already in map"); + return false; + } + + nodeMap.insert(std::make_pair(id, this)); + + assert(!inited); + inited = true; + + return true; + } + ZT_Node* findNode(int64_t nodeId) { ZeroTier::Mutex::Lock lock(nodeMapMutex); NodeMap::iterator found = nodeMap.find(nodeId); - if(found != nodeMap.end()) - { - JniRef *ref = found->second; - return ref->node; + + assert(found != nodeMap.end()); + + JniRef *ref = found->second; + + assert(ref); + + return ref->node; + } + + JniRef *removeRef(int64_t nodeId) { + + ZeroTier::Mutex::Lock lock(nodeMapMutex); + + NodeMap::iterator found = nodeMap.find(nodeId); + + if (found == nodeMap.end()) { + return nullptr; } - return NULL; + + JniRef *ref = found->second; + + assert(ref); + + nodeMap.erase(nodeId); + + return ref; } } +#ifdef __cplusplus +extern "C" { +#endif + JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { - lookup.setJavaVM(vm); + setupJNICache(vm); return JNI_VERSION_1_6; } JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { - + teardownJNICache(vm); } /* * Class: com_zerotier_sdk_Node * Method: node_init - * Signature: (J)Lcom/zerotier/sdk/ResultCode; + * Signature: (JLcom/zerotier/sdk/DataStoreGetListener;Lcom/zerotier/sdk/DataStorePutListener;Lcom/zerotier/sdk/PacketSender;Lcom/zerotier/sdk/EventListener;Lcom/zerotier/sdk/VirtualNetworkFrameListener;Lcom/zerotier/sdk/VirtualNetworkConfigListener;Lcom/zerotier/sdk/PathChecker;)Lcom/zerotier/sdk/ResultCode; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( - JNIEnv *env, jobject obj, jlong now) + JNIEnv *env, jobject obj, jlong now, jobject dataStoreGetListener, + jobject dataStorePutListener, jobject packetSender, jobject eventListener, + jobject frameListener, jobject configListener, + jobject pathChecker) { LOGV("Creating ZT_Node struct"); - jobject resultObject = createResultObject(env, ZT_RESULT_OK); + jobject resultObject = ResultCode_RESULT_OK_enum; + + JavaVM *vm; + GETJAVAVM(env, vm); + + assert(dataStoreGetListener != NULL); + assert(dataStorePutListener != NULL); + assert(packetSender != NULL); + assert(frameListener != NULL); + assert(configListener != NULL); + assert(eventListener != NULL); + // + // OPTIONAL, pathChecker may be NULL + // +// assert(pathChecker != NULL); + + ZT_Node_Callbacks callbacks{}; + callbacks.stateGetFunction = &StateGetFunction; + callbacks.statePutFunction = &StatePutFunction; + callbacks.wirePacketSendFunction = &WirePacketSendFunction; + callbacks.virtualNetworkFrameFunction = &VirtualNetworkFrameFunctionCallback; + callbacks.virtualNetworkConfigFunction = &VirtualNetworkConfigFunctionCallback; + callbacks.eventCallback = &EventCallback; + callbacks.pathCheckFunction = &PathCheckFunction; + callbacks.pathLookupFunction = &PathLookupFunction; + + // + // a bit of a confusing dance here where ref and node both know about each other + // + JniRef *ref = new JniRef( + now, + vm, + dataStoreGetListener, + dataStorePutListener, + packetSender, + eventListener, + frameListener, + configListener, + pathChecker); ZT_Node *node; - JniRef *ref = new JniRef; - ref->id = (int64_t)now; - env->GetJavaVM(&ref->jvm); - - jclass cls = env->GetObjectClass(obj); - jfieldID fid = lookup.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 = lookup.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 = lookup.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 = lookup.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 = lookup.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 = lookup.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); - - fid = lookup.findField( - cls, "pathChecker", "Lcom/zerotier/sdk/PathChecker;"); - if(fid == NULL) - { - LOGE("no path checker?"); - return NULL; - } - - tmp = env->GetObjectField(obj, fid); - if(tmp != NULL) - { - ref->pathChecker = env->NewGlobalRef(tmp); - } - - ref->callbacks->stateGetFunction = &StateGetFunction; - ref->callbacks->statePutFunction = &StatePutFunction; - ref->callbacks->wirePacketSendFunction = &WirePacketSendFunction; - ref->callbacks->virtualNetworkFrameFunction = &VirtualNetworkFrameFunctionCallback; - ref->callbacks->virtualNetworkConfigFunction = &VirtualNetworkConfigFunctionCallback; - ref->callbacks->eventCallback = &EventCallback; - ref->callbacks->pathCheckFunction = &PathCheckFunction; - ref->callbacks->pathLookupFunction = &PathLookupFunction; - ZT_ResultCode rc = ZT_Node_new( &node, ref, NULL, - ref->callbacks, + &callbacks, (int64_t)now); + if (env->ExceptionCheck()) { + LOGE("Exception creating Node"); + if(node) + { + ZT_Node_delete(node); + node = NULL; + } + delete ref; + ref = NULL; + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } if(rc != ZT_RESULT_OK) { LOGE("Error creating Node: %d", rc); resultObject = createResultObject(env, rc); + if (env->ExceptionCheck() || resultObject == NULL) { + return NULL; + } + if(node) { ZT_Node_delete(node); @@ -891,13 +860,29 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( return resultObject; } - ZeroTier::Mutex::Lock lock(nodeMapMutex); + // + // node is now updated + // ref->node = node; - nodeMap.insert(std::make_pair(ref->id, ref)); + + if (!ref->finishInitializing()) { + LOGE("finishInitializing() failed"); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } return resultObject; } +/* + * Class: com_zerotier_sdk_Node + * Method: node_isInited + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_com_zerotier_sdk_Node_node_1isInited + (JNIEnv *env, jobject obj, jlong nodeId) { + return isInited(nodeId); +} + /* * Class: com_zerotier_sdk_Node * Method: node_delete @@ -909,26 +894,15 @@ JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete( LOGV("Destroying ZT_Node struct"); int64_t nodeId = (int64_t)id; - NodeMap::iterator found; - { - ZeroTier::Mutex::Lock lock(nodeMapMutex); - found = nodeMap.find(nodeId); + JniRef *ref = removeRef(nodeId); + + if (!ref) { + return; } - if(found != nodeMap.end()) - { - JniRef *ref = found->second; - nodeMap.erase(found); + ZT_Node_delete(ref->node); - ZT_Node_delete(ref->node); - - delete ref; - ref = NULL; - } - else - { - LOGE("Attempted to delete a node that doesn't exist!"); - } + delete ref; } /* @@ -951,17 +925,12 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame( int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - return createResultObject(env, ZT_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, ZT_RESULT_FATAL_ERROR_INTERNAL); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; } int64_t now = (int64_t)in_now; @@ -973,6 +942,9 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame( unsigned int frameLength = env->GetArrayLength(in_frameData); void *frameData = env->GetPrimitiveArrayCritical(in_frameData, NULL); + // + // need local copy of frameData because arbitrary code may run in ZT_Node_processVirtualNetworkFrame and no other JNI work may happen between GetPrimitiveArrayCritical / ReleasePrimitiveArrayCritical + // void *localData = malloc(frameLength); memcpy(localData, frameData, frameLength); env->ReleasePrimitiveArrayCritical(in_frameData, frameData, 0); @@ -991,6 +963,16 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame( (const void*)localData, frameLength, &nextBackgroundTaskDeadline); + if (env->ExceptionCheck()) { + LOGE("Exception calling ZT_Node_processVirtualNetworkFrame"); + free(localData); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } + if (rc != ZT_RESULT_OK) { + LOGE("ZT_Node_processVirtualNetworkFrame returned: %d", rc); + } + + free(localData); jlong *outDeadline = (jlong*)env->GetPrimitiveArrayCritical(out_nextBackgroundTaskDeadline, NULL); outDeadline[0] = (jlong)nextBackgroundTaskDeadline; @@ -1002,7 +984,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame( /* * Class: com_zerotier_sdk_Node * Method: processWirePacket - * Signature: (JJJLjava/net/InetSocketAddress;I[B[J)Lcom/zerotier/sdk/ResultCode; + * Signature: (JJJLjava/net/InetSocketAddress;[B[J)Lcom/zerotier/sdk/ResultCode; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( JNIEnv *env, jobject obj, @@ -1015,122 +997,31 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( { int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - LOGE("Couldn't find a valid node!"); - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } unsigned int nbtd_len = (unsigned int)env->GetArrayLength(out_nextBackgroundTaskDeadline); if(nbtd_len < 1) { LOGE("nbtd_len < 1"); - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; } int64_t now = (int64_t)in_now; - // get the java.net.InetSocketAddress class and getAddress() method - jclass inetAddressClass = lookup.findClass("java/net/InetAddress"); - if(inetAddressClass == NULL) - { - LOGE("Can't find InetAddress class"); - // can't find java.net.InetAddress - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); + sockaddr_storage remoteAddress = fromSocketAddressObject(env, in_remoteAddress); + if (env->ExceptionCheck() || isSocketAddressEmpty(remoteAddress)) { + return NULL; } - jmethodID getAddressMethod = lookup.findMethod( - inetAddressClass, "getAddress", "()[B"); - if(getAddressMethod == NULL) - { - // cant find InetAddress.getAddress() - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } - - jclass InetSocketAddressClass = lookup.findClass("java/net/InetSocketAddress"); - if(InetSocketAddressClass == NULL) - { - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } - - jmethodID inetSockGetAddressMethod = lookup.findMethod( - InetSocketAddressClass, "getAddress", "()Ljava/net/InetAddress;"); - - jobject remoteAddrObject = env->CallObjectMethod(in_remoteAddress, inetSockGetAddressMethod); - - if(remoteAddrObject == NULL) - { - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } - - jmethodID inetSock_getPort = lookup.findMethod( - InetSocketAddressClass, "getPort", "()I"); - - if(env->ExceptionCheck() || inetSock_getPort == NULL) - { - LOGE("Couldn't find getPort method on InetSocketAddress"); - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } - - // call InetSocketAddress.getPort() - int remotePort = env->CallIntMethod(in_remoteAddress, inetSock_getPort); - if(env->ExceptionCheck()) - { - LOGE("Exception calling InetSocketAddress.getPort()"); - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } - - // Call InetAddress.getAddress() - jbyteArray remoteAddressArray = (jbyteArray)env->CallObjectMethod(remoteAddrObject, getAddressMethod); - if(remoteAddressArray == NULL) - { - LOGE("Unable to call getAddress()"); - // unable to call getAddress() - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } - - unsigned int addrSize = env->GetArrayLength(remoteAddressArray); - - - // get the address bytes - jbyte *addr = (jbyte*)env->GetPrimitiveArrayCritical(remoteAddressArray, NULL); - sockaddr_storage remoteAddress = {}; - - if(addrSize == 16) - { - // IPV6 address - sockaddr_in6 ipv6 = {}; - ipv6.sin6_family = AF_INET6; - ipv6.sin6_port = htons(remotePort); - 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; - ipv4.sin_port = htons(remotePort); - memcpy(&ipv4.sin_addr, addr, 4); - memcpy(&remoteAddress, &ipv4, sizeof(sockaddr_in)); - } - else - { - LOGE("Unknown IP version"); - // unknown address type - env->ReleasePrimitiveArrayCritical(remoteAddressArray, addr, 0); - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } - env->ReleasePrimitiveArrayCritical(remoteAddressArray, addr, 0); - unsigned int packetLength = (unsigned int)env->GetArrayLength(in_packetData); if(packetLength == 0) { LOGE("Empty packet?!?"); - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; } void *packetData = env->GetPrimitiveArrayCritical(in_packetData, NULL); + // + // need local copy of packetData because arbitrary code may run in ZT_Node_processWirePacket and no other JNI work may happen between GetPrimitiveArrayCritical / ReleasePrimitiveArrayCritical + // void *localData = malloc(packetLength); memcpy(localData, packetData, packetLength); env->ReleasePrimitiveArrayCritical(in_packetData, packetData, 0); @@ -1146,6 +1037,11 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( localData, packetLength, &nextBackgroundTaskDeadline); + if (env->ExceptionCheck()) { + LOGE("Exception calling ZT_Node_processWirePacket"); + free(localData); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } if(rc != ZT_RESULT_OK) { LOGE("ZT_Node_processWirePacket returned: %d", rc); @@ -1173,22 +1069,24 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks( { int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } unsigned int nbtd_len = env->GetArrayLength(out_nextBackgroundTaskDeadline); if(nbtd_len < 1) { - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; } int64_t now = (int64_t)in_now; int64_t nextBackgroundTaskDeadline = 0; ZT_ResultCode rc = ZT_Node_processBackgroundTasks(node, NULL, now, &nextBackgroundTaskDeadline); + if (env->ExceptionCheck()) { + LOGE("Exception calling ZT_Node_processBackgroundTasks"); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } + if (rc != ZT_RESULT_OK) { + LOGE("ZT_Node_processBackgroundTasks returned: %d", rc); + } jlong *outDeadline = (jlong*)env->GetPrimitiveArrayCritical(out_nextBackgroundTaskDeadline, NULL); outDeadline[0] = (jlong)nextBackgroundTaskDeadline; @@ -1207,15 +1105,13 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_join( { int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } - uint64_t nwid = (uint64_t)in_nwid; ZT_ResultCode rc = ZT_Node_join(node, nwid, NULL, NULL); + if (env->ExceptionCheck()) { + LOGE("Exception calling ZT_Node_join"); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } return createResultObject(env, rc); } @@ -1230,15 +1126,14 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave( { int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } uint64_t nwid = (uint64_t)in_nwid; ZT_ResultCode rc = ZT_Node_leave(node, nwid, NULL, NULL); + if (env->ExceptionCheck()) { + LOGE("Exception calling ZT_Node_leave"); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } return createResultObject(env, rc); } @@ -1257,11 +1152,6 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe( { int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } uint64_t nwid = (uint64_t)in_nwid; uint64_t multicastGroup = (uint64_t)in_multicastGroup; @@ -1269,6 +1159,10 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe( ZT_ResultCode rc = ZT_Node_multicastSubscribe( node, NULL, nwid, multicastGroup, multicastAdi); + if (env->ExceptionCheck()) { + LOGE("Exception calling ZT_Node_multicastSubscribe"); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } return createResultObject(env, rc); } @@ -1287,11 +1181,6 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe( { int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } uint64_t nwid = (uint64_t)in_nwid; uint64_t multicastGroup = (uint64_t)in_multicastGroup; @@ -1299,6 +1188,10 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe( ZT_ResultCode rc = ZT_Node_multicastUnsubscribe( node, nwid, multicastGroup, multicastAdi); + if (env->ExceptionCheck()) { + LOGE("Exception calling ZT_Node_multicastUnsubscribe"); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } return createResultObject(env, rc); } @@ -1316,15 +1209,16 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_orbit( { int64_t nodeId = (int64_t)id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } uint64_t moonWorldId = (uint64_t)in_moonWorldId; uint64_t moonSeed = (uint64_t)in_moonSeed; ZT_ResultCode rc = ZT_Node_orbit(node, NULL, moonWorldId, moonSeed); + if (env->ExceptionCheck()) { + LOGE("Exception calling ZT_Node_orbit"); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } + return createResultObject(env, rc); } @@ -1340,14 +1234,15 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_deorbit( { int64_t nodeId = (int64_t)id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL); - } uint64_t moonWorldId = (uint64_t)in_moonWorldId; ZT_ResultCode rc = ZT_Node_deorbit(node, NULL, moonWorldId); + if (env->ExceptionCheck()) { + LOGE("Exception calling ZT_Node_deorbit"); + return ResultCode_RESULT_FATAL_ERROR_INTERNAL_enum; + } + return createResultObject(env, rc); } @@ -1361,11 +1256,6 @@ JNIEXPORT jlong JNICALL Java_com_zerotier_sdk_Node_address( { int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - return 0; - } uint64_t address = ZT_Node_address(node); return (jlong)address; @@ -1381,105 +1271,29 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status { int64_t nodeId = (int64_t) id; ZT_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 = lookup.findClass("com/zerotier/sdk/NodeStatus"); - if(nodeStatusClass == NULL) - { - return NULL; - } - - nodeStatusConstructor = lookup.findMethod( - nodeStatusClass, "", "()V"); - if(nodeStatusConstructor == NULL) - { - return NULL; - } - - jobject nodeStatusObj = env->NewObject(nodeStatusClass, nodeStatusConstructor); - if(nodeStatusObj == NULL) - { - return NULL; - } ZT_NodeStatus nodeStatus; ZT_Node_status(node, &nodeStatus); - jfieldID addressField = NULL; - jfieldID publicIdentityField = NULL; - jfieldID secretIdentityField = NULL; - jfieldID onlineField = NULL; - - addressField = lookup.findField(nodeStatusClass, "address", "J"); - if(addressField == NULL) - { - return NULL; - } - - publicIdentityField = lookup.findField(nodeStatusClass, "publicIdentity", "Ljava/lang/String;"); - if(publicIdentityField == NULL) - { - return NULL; - } - - secretIdentityField = lookup.findField(nodeStatusClass, "secretIdentity", "Ljava/lang/String;"); - if(secretIdentityField == NULL) - { - return NULL; - } - - onlineField = lookup.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; + return newNodeStatus(env, nodeStatus); } /* * Class: com_zerotier_sdk_Node * Method: networkConfig - * Signature: (J)Lcom/zerotier/sdk/VirtualNetworkConfig; + * Signature: (JJ)Lcom/zerotier/sdk/VirtualNetworkConfig; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networkConfig( JNIEnv *env, jobject obj, jlong id, jlong nwid) { int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - return 0; - } ZT_VirtualNetworkConfig *vnetConfig = ZT_Node_networkConfig(node, nwid); + if (vnetConfig == NULL) { + LOGE("vnetConfig == NULL"); + return NULL; + } jobject vnetConfigObject = newNetworkConfig(env, *vnetConfig); @@ -1491,7 +1305,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networkConfig( /* * Class: com_zerotier_sdk_Node * Method: version - * Signature: (J)Lcom/zerotier/sdk/Version; + * Signature: ()Lcom/zerotier/sdk/Version; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_version( JNIEnv *env, jobject obj) @@ -1515,11 +1329,6 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers( { int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - return 0; - } ZT_PeerList *peerList = ZT_Node_peers(node); @@ -1529,44 +1338,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers( return NULL; } - int peerCount = peerList->peerCount * 100; - LOGV("Ensure Local Capacity: %d", peerCount); - if(env->EnsureLocalCapacity(peerCount)) - { - LOGE("EnsureLocalCapacity failed!!"); - ZT_Node_freeQueryResult(node, peerList); - return NULL; - } - - jclass peerClass = lookup.findClass("com/zerotier/sdk/Peer"); - if(env->ExceptionCheck() || peerClass == NULL) - { - LOGE("Error finding Peer class"); - ZT_Node_freeQueryResult(node, peerList); - return NULL; - } - - jobjectArray peerArrayObj = env->NewObjectArray( - peerList->peerCount, peerClass, NULL); - - if(env->ExceptionCheck() || peerArrayObj == NULL) - { - LOGE("Error creating Peer[] array"); - ZT_Node_freeQueryResult(node, peerList); - return NULL; - } - - - for(unsigned int i = 0; i < peerList->peerCount; ++i) - { - jobject peerObj = newPeer(env, peerList->peers[i]); - env->SetObjectArrayElement(peerArrayObj, i, peerObj); - if(env->ExceptionCheck()) - { - LOGE("Error assigning Peer object to array"); - break; - } - } + jobjectArray peerArrayObj = newPeerArray(env, peerList->peers, peerList->peerCount); ZT_Node_freeQueryResult(node, peerList); peerList = NULL; @@ -1576,53 +1348,23 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers( /* * Class: com_zerotier_sdk_Node - * Method: networks + * Method: networkConfigs * Signature: (J)[Lcom/zerotier/sdk/VirtualNetworkConfig; */ -JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_networks( +JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_networkConfigs( JNIEnv *env, jobject obj, jlong id) { int64_t nodeId = (int64_t) id; ZT_Node *node = findNode(nodeId); - if(node == NULL) - { - // cannot find valid node. We should never get here. - return 0; - } ZT_VirtualNetworkList *networkList = ZT_Node_networks(node); if(networkList == NULL) { + LOGE("ZT_Node_networks returned NULL"); return NULL; } - jclass vnetConfigClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfig"); - if(env->ExceptionCheck() || vnetConfigClass == NULL) - { - LOGE("Error finding VirtualNetworkConfig class"); - ZT_Node_freeQueryResult(node, networkList); - return NULL; - } - - jobjectArray networkListObject = env->NewObjectArray( - networkList->networkCount, vnetConfigClass, NULL); - if(env->ExceptionCheck() || networkListObject == NULL) - { - LOGE("Error creating VirtualNetworkConfig[] array"); - ZT_Node_freeQueryResult(node, networkList); - return NULL; - } - - for(unsigned int i = 0; i < networkList->networkCount; ++i) - { - jobject networkObject = newNetworkConfig(env, networkList->networks[i]); - env->SetObjectArrayElement(networkListObject, i, networkObject); - if(env->ExceptionCheck()) - { - LOGE("Error assigning VirtualNetworkConfig object to array"); - break; - } - } + jobjectArray networkListObject = newVirtualNetworkConfigArray(env, networkList->networks, networkList->networkCount); ZT_Node_freeQueryResult(node, networkList); @@ -1631,4 +1373,4 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_networks( #ifdef __cplusplus } // extern "C" -#endif \ No newline at end of file +#endif diff --git a/java/jni/com_zerotierone_sdk_Node.h b/java/jni/com_zerotierone_sdk_Node.h index 8487d8af2..a439f4129 100644 --- a/java/jni/com_zerotierone_sdk_Node.h +++ b/java/jni/com_zerotierone_sdk_Node.h @@ -10,9 +10,17 @@ extern "C" { /* * Class: com_zerotier_sdk_Node * Method: node_init - * Signature: (J)Lcom/zerotier/sdk/ResultCode; + * Signature: (JLcom/zerotier/sdk/DataStoreGetListener;Lcom/zerotier/sdk/DataStorePutListener;Lcom/zerotier/sdk/PacketSender;Lcom/zerotier/sdk/EventListener;Lcom/zerotier/sdk/VirtualNetworkFrameListener;Lcom/zerotier/sdk/VirtualNetworkConfigListener;Lcom/zerotier/sdk/PathChecker;)Lcom/zerotier/sdk/ResultCode; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init + (JNIEnv *, jobject, jlong, jobject, jobject, jobject, jobject, jobject, jobject, jobject); + +/* + * Class: com_zerotier_sdk_Node + * Method: node_isInited + * Signature: (J)Z; + */ +JNIEXPORT jboolean JNICALL Java_com_zerotier_sdk_Node_node_1isInited (JNIEnv *, jobject, jlong); /* @@ -34,7 +42,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame /* * Class: com_zerotier_sdk_Node * Method: processWirePacket - * Signature: (JJLjava/net/InetSockAddress;Ljava/net/InetSockAddress;[B[J)Lcom/zerotier/sdk/ResultCode; + * Signature: (JJJLjava/net/InetSockAddress;[B[J)Lcom/zerotier/sdk/ResultCode; */ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket (JNIEnv *, jobject, jlong, jlong, jlong, jobject, jbyteArray, jlongArray); @@ -121,10 +129,10 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers /* * Class: com_zerotier_sdk_Node - * Method: networks + * Method: networkConfigs * Signature: (J)[Lcom/zerotier/sdk/VirtualNetworkConfig; */ -JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_networks +JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_networkConfigs (JNIEnv *, jobject, jlong); #ifdef __cplusplus diff --git a/java/src/com/zerotier/sdk/DataStoreGetListener.java b/java/src/com/zerotier/sdk/DataStoreGetListener.java index 317511e0c..105b14c55 100644 --- a/java/src/com/zerotier/sdk/DataStoreGetListener.java +++ b/java/src/com/zerotier/sdk/DataStoreGetListener.java @@ -24,6 +24,7 @@ * 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 { @@ -48,7 +49,7 @@ public interface DataStoreGetListener { * @param out_buffer buffer to put the object in * @return size of the object */ - public long onDataStoreGet( + long onDataStoreGet( String name, byte[] out_buffer); } diff --git a/java/src/com/zerotier/sdk/DataStorePutListener.java b/java/src/com/zerotier/sdk/DataStorePutListener.java index 77e55027e..0fa8e19c1 100644 --- a/java/src/com/zerotier/sdk/DataStorePutListener.java +++ b/java/src/com/zerotier/sdk/DataStorePutListener.java @@ -24,6 +24,7 @@ * 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 { @@ -43,7 +44,7 @@ public interface DataStorePutListener { * @param secure set to user read/write only. * @return 0 on success. */ - public int onDataStorePut( + int onDataStorePut( String name, byte[] buffer, boolean secure); @@ -54,6 +55,6 @@ public interface DataStorePutListener { * @param name Object name * @return 0 on success. */ - public int onDelete( + int onDelete( String name); } diff --git a/java/src/com/zerotier/sdk/Event.java b/java/src/com/zerotier/sdk/Event.java index 22d350e1e..fbc016c66 100644 --- a/java/src/com/zerotier/sdk/Event.java +++ b/java/src/com/zerotier/sdk/Event.java @@ -27,26 +27,32 @@ package com.zerotier.sdk; +/** + * Status codes sent to status update callback when things happen + * + * Defined in ZeroTierOne.h as ZT_Event + */ 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, + EVENT_UP(0), /** * Node is offline -- network does not seem to be reachable by any available strategy */ - EVENT_OFFLINE, + EVENT_OFFLINE(1), /** * Node is online -- at least one upstream node appears reachable * * Meta-data: none */ - EVENT_ONLINE, + EVENT_ONLINE(2), /** * Node is shutting down @@ -55,7 +61,7 @@ public enum Event { * It's done for convenience, since cleaning up other state in the event * handler may appear more idiomatic.

*/ - EVENT_DOWN, + EVENT_DOWN(3), /** * Your identity has collided with another node's ZeroTier address @@ -85,7 +91,7 @@ public enum Event { * 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?

*/ - EVENT_FATAL_ERROR_IDENTITY_COLLISION, + EVENT_FATAL_ERROR_IDENTITY_COLLISION(4), /** * Trace (debugging) message @@ -94,5 +100,55 @@ public enum Event { * *

Meta-data: {@link String}, TRACE message

*/ - EVENT_TRACE -} \ No newline at end of file + EVENT_TRACE(5), + + /** + * VERB_USER_MESSAGE received + * + * These are generated when a VERB_USER_MESSAGE packet is received via + * ZeroTier VL1. + */ + EVENT_USER_MESSAGE(6), + + /** + * Remote trace received + * + * These are generated when a VERB_REMOTE_TRACE is received. Note + * that any node can fling one of these at us. It is your responsibility + * to filter and determine if it's worth paying attention to. If it's + * not just drop it. Most nodes that are not active controllers ignore + * these, and controllers only save them if they pertain to networks + * with remote tracing enabled. + */ + EVENT_REMOTE_TRACE(7); + + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private final int id; + + Event(int id) { + this.id = id; + } + + public static Event fromInt(int id) { + switch (id) { + case 0: + return EVENT_UP; + case 1: + return EVENT_OFFLINE; + case 2: + return EVENT_ONLINE; + case 3: + return EVENT_DOWN; + case 4: + return EVENT_FATAL_ERROR_IDENTITY_COLLISION; + case 5: + return EVENT_TRACE; + case 6: + return EVENT_USER_MESSAGE; + case 7: + return EVENT_REMOTE_TRACE; + default: + throw new RuntimeException("Unhandled value: " + id); + } + } +} diff --git a/java/src/com/zerotier/sdk/EventListener.java b/java/src/com/zerotier/sdk/EventListener.java index 91050aaa9..88fb8afc3 100644 --- a/java/src/com/zerotier/sdk/EventListener.java +++ b/java/src/com/zerotier/sdk/EventListener.java @@ -27,19 +27,17 @@ 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); + void onEvent(Event event); /** * Trace messages @@ -48,5 +46,5 @@ public interface EventListener { * * @param message the trace message */ - public void onTrace(String message); + void onTrace(String message); } diff --git a/java/src/com/zerotier/sdk/NativeUtils.java b/java/src/com/zerotier/sdk/NativeUtils.java deleted file mode 100644 index 4932a6c71..000000000 --- a/java/src/com/zerotier/sdk/NativeUtils.java +++ /dev/null @@ -1,93 +0,0 @@ -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 , 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 prefix 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 index 1b3a4901f..a3f3ab470 100644 --- a/java/src/com/zerotier/sdk/Node.java +++ b/java/src/com/zerotier/sdk/Node.java @@ -28,95 +28,72 @@ 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(); - } - } - } + static { + System.loadLibrary("ZeroTierOneJNI"); + } 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; - private final PathChecker pathChecker; + private final long nodeId; /** * Create a new ZeroTier One node * + * @param now Current clock in milliseconds + */ + public Node(long now) { + this.nodeId = now; + } + + /** + * Init a new ZeroTier One node + * *

Note that this can take a few seconds the first time it's called, as it * will generate an identity.

* - * @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 instance of the {@link DataStorePutListener} interface called to put objects in persistent storage. This instance must be unique per Node object. - * @param sender + * @param sender User written instance of the {@link PacketSender} interface to send ZeroTier packets out over the wire. * @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 frameListener User written instance of the {@link VirtualNetworkFrameListener} interface to send a frame out to a virtual network port. * @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. * @param pathChecker User written instance of the {@link PathChecker} interface. Not required and can be null. */ - public Node(long now, - DataStoreGetListener getListener, - DataStorePutListener putListener, - PacketSender sender, - EventListener eventListener, - VirtualNetworkFrameListener frameListener, - VirtualNetworkConfigListener configListener, - PathChecker pathChecker) throws NodeException - { - this.nodeId = now; - - this.getListener = getListener; - this.putListener = putListener; - this.sender = sender; - this.eventListener = eventListener; - this.frameListener = frameListener; - this.configListener = configListener; - this.pathChecker = pathChecker; - - ResultCode rc = node_init(now); - if(rc != ResultCode.RESULT_OK) - { - // TODO: Throw Exception + public ResultCode init( + DataStoreGetListener getListener, + DataStorePutListener putListener, + PacketSender sender, + EventListener eventListener, + VirtualNetworkFrameListener frameListener, + VirtualNetworkConfigListener configListener, + PathChecker pathChecker) throws NodeException { + ResultCode rc = node_init( + nodeId, + getListener, + putListener, + sender, + eventListener, + frameListener, + configListener, + pathChecker); + if(rc != ResultCode.RESULT_OK) { throw new NodeException(rc.toString()); } - } + return rc; + } + + public boolean isInited() { + return node_isInited(nodeId); + } /** * Close this Node. @@ -124,15 +101,12 @@ public class Node { *

The Node object can no longer be used once this method is called.

*/ public void close() { - if(nodeId != -1) { - node_delete(nodeId); - nodeId = -1; - } + node_delete(nodeId); } @Override - protected void finalize() { - close(); + public String toString() { + return "Node(" + nodeId + ")"; } /** @@ -166,6 +140,7 @@ public class Node { * Process a packet received from the physical wire * * @param now Current clock in milliseconds + * @param localSocket Local socket or -1 * @param remoteAddress Origin of packet * @param packetData Packet data * @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks() @@ -392,8 +367,8 @@ public class Node { * * @return List of networks or NULL on failure */ - public VirtualNetworkConfig[] networks() { - return networks(nodeId); + public VirtualNetworkConfig[] networkConfigs() { + return networkConfigs(nodeId); } /** @@ -408,7 +383,17 @@ public class Node { // // function declarations for JNI // - private native ResultCode node_init(long now); + private native ResultCode node_init( + long nodeId, + DataStoreGetListener dataStoreGetListener, + DataStorePutListener dataStorePutListener, + PacketSender packetSender, + EventListener eventListener, + VirtualNetworkFrameListener virtualNetworkFrameListener, + VirtualNetworkConfigListener virtualNetworkConfigListener, + PathChecker pathChecker); + + private native boolean node_isInited(long nodeId); private native void node_delete(long nodeId); @@ -471,5 +456,5 @@ public class Node { private native Peer[] peers(long nodeId); - private native VirtualNetworkConfig[] networks(long nodeId); -} \ No newline at end of file + private native VirtualNetworkConfig[] networkConfigs(long nodeId); +} diff --git a/java/src/com/zerotier/sdk/NodeException.java b/java/src/com/zerotier/sdk/NodeException.java index 1fdef72f8..beeb06063 100644 --- a/java/src/com/zerotier/sdk/NodeException.java +++ b/java/src/com/zerotier/sdk/NodeException.java @@ -27,10 +27,11 @@ package com.zerotier.sdk; -import java.lang.RuntimeException; +public class NodeException extends Exception { -public class NodeException extends RuntimeException { + private static final long serialVersionUID = 6268040509883125819L; + public 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 index 11e49ade1..1172650b1 100644 --- a/java/src/com/zerotier/sdk/NodeStatus.java +++ b/java/src/com/zerotier/sdk/NodeStatus.java @@ -27,43 +27,64 @@ package com.zerotier.sdk; -public final class NodeStatus { - private long address; - private String publicIdentity; - private String secretIdentity; - private boolean online; +import com.zerotier.sdk.util.StringUtils; - private NodeStatus() {} +/** + * Current node status + * + * Defined in ZeroTierOne.h as ZT_NodeStatus + */ +public class NodeStatus { + + private final long address; + + private final String publicIdentity; + + private final String secretIdentity; + + private final boolean online; + + public NodeStatus(long address, String publicIdentity, String secretIdentity, boolean online) { + this.address = address; + this.publicIdentity = publicIdentity; + this.secretIdentity = secretIdentity; + this.online = online; + } + + @Override + public String toString() { + return "NodeStatus(" + StringUtils.addressToString(address) + ", " + publicIdentity + ", " + secretIdentity + ", " + online + ")"; + } /** * 40-bit ZeroTier address of this node */ - public final long getAddress() { - return address; - } + public long getAddress() { + return address; + } /** * Public identity in string-serialized form (safe to send to others) * *

This identity will remain valid as long as the node exists.

*/ - public final String getPublicIdentity() { - return publicIdentity; - } + public String getPublicIdentity() { + return publicIdentity; + } /** * Full identity including secret key in string-serialized form * *

This identity will remain valid as long as the node exists.

*/ - public final String getSecretIdentity() { - return secretIdentity; - } + public String getSecretIdentity() { + return secretIdentity; + } /** * True if some kind of connectivity appears available */ - public final boolean isOnline() { - return online; - } -} \ No newline at end of file + public boolean isOnline() { + return online; + } +} diff --git a/java/src/com/zerotier/sdk/PacketSender.java b/java/src/com/zerotier/sdk/PacketSender.java index 06ec01bcc..893824a08 100644 --- a/java/src/com/zerotier/sdk/PacketSender.java +++ b/java/src/com/zerotier/sdk/PacketSender.java @@ -24,12 +24,14 @@ * 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 * @@ -40,9 +42,10 @@ public interface PacketSender { * @param localSocket socket file descriptor to send from. Set to -1 if not specified. * @param remoteAddr {@link InetSocketAddress} to send to * @param packetData data to send + * @param ttl TTL is ignored * @return 0 on success, any error code on failure. */ - public int onSendPacketRequested( + int onSendPacketRequested( long localSocket, InetSocketAddress remoteAddr, byte[] packetData, diff --git a/java/src/com/zerotier/sdk/PathChecker.java b/java/src/com/zerotier/sdk/PathChecker.java index 6bf31df2b..cfc97d60e 100644 --- a/java/src/com/zerotier/sdk/PathChecker.java +++ b/java/src/com/zerotier/sdk/PathChecker.java @@ -8,6 +8,7 @@ package com.zerotier.sdk; import java.net.InetSocketAddress; public interface PathChecker { + /** * Callback to check whether a path should be used for ZeroTier traffic * @@ -28,6 +29,7 @@ public interface PathChecker { * @param ztAddress ZeroTier address or 0 for none/any * @param localSocket Local interface socket. -1 if unspecified * @param remoteAddress remote address + * @return true if the path should be used */ boolean onPathCheck(long ztAddress, long localSocket, InetSocketAddress remoteAddress); diff --git a/java/src/com/zerotier/sdk/Peer.java b/java/src/com/zerotier/sdk/Peer.java index eb3d71300..e3d544381 100644 --- a/java/src/com/zerotier/sdk/Peer.java +++ b/java/src/com/zerotier/sdk/Peer.java @@ -27,68 +27,92 @@ package com.zerotier.sdk; -import java.util.ArrayList; +import com.zerotier.sdk.util.StringUtils; + +import java.util.Arrays; /** - * Peer status result + * Peer status result buffer + * + * Defined in ZeroTierOne.h as ZT_Peer */ -public final class Peer { - private long address; - private int versionMajor; - private int versionMinor; - private int versionRev; - private int latency; - private PeerRole role; - private PeerPhysicalPath[] paths; +public class Peer { - private Peer() {} + private final long address; + + private final int versionMajor; + + private final int versionMinor; + + private final int versionRev; + + private final int latency; + + private final PeerRole role; + + private final PeerPhysicalPath[] paths; + + public Peer(long address, int versionMajor, int versionMinor, int versionRev, int latency, PeerRole role, PeerPhysicalPath[] paths) { + this.address = address; + this.versionMajor = versionMajor; + this.versionMinor = versionMinor; + this.versionRev = versionRev; + this.latency = latency; + this.role = role; + this.paths = paths; + } + + @Override + public String toString() { + return "Peer(" + StringUtils.addressToString(address) + ", " + versionMajor + ", " + versionMinor + ", " + versionRev + ", " + latency + ", " + role + ", " + Arrays.toString(paths) + ")"; + } /** * ZeroTier address (40 bits) */ - public final long address() { + public long getAddress() { return address; } /** * Remote major version or -1 if not known */ - public final int versionMajor() { + public int getVersionMajor() { return versionMajor; } /** * Remote minor version or -1 if not known */ - public final int versionMinor() { + public int getVersionMinor() { return versionMinor; } /** * Remote revision or -1 if not known */ - public final int versionRev() { + public int getVersionRev() { return versionRev; } /** * Last measured latency in milliseconds or zero if unknown */ - public final int latency() { + public int getLatency() { return latency; } /** * What trust hierarchy role does this device have? */ - public final PeerRole role() { + public PeerRole getRole() { return role; } /** * Known network paths to peer */ - public final PeerPhysicalPath[] paths() { + public PeerPhysicalPath[] getPaths() { 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 index 3f9a86128..f6d326425 100644 --- a/java/src/com/zerotier/sdk/PeerPhysicalPath.java +++ b/java/src/com/zerotier/sdk/PeerPhysicalPath.java @@ -31,48 +31,62 @@ import java.net.InetSocketAddress; /** * Physical network path to a peer + * + * Defined in ZeroTierOne.h as ZT_PeerPhysicalPath */ -public final class PeerPhysicalPath { - private InetSocketAddress address; - private long lastSend; - private long lastReceive; - private boolean fixed; - private boolean preferred; +public class PeerPhysicalPath { - private PeerPhysicalPath() {} + private final InetSocketAddress address; + + private final long lastSend; + + private final long lastReceive; + + private final boolean preferred; + + public PeerPhysicalPath(InetSocketAddress address, long lastSend, long lastReceive, boolean preferred) { + this.address = address; + if (lastSend < 0) { + throw new RuntimeException("lastSend < 0: " + lastSend); + } + this.lastSend = lastSend; + if (lastReceive < 0) { + throw new RuntimeException("lastReceive < 0: " + lastReceive); + } + this.lastReceive = lastReceive; + this.preferred = preferred; + } + + @Override + public String toString() { + return "PeerPhysicalPath(" + address + ", " + lastSend + ", " + lastReceive + ", " + preferred + ")"; + } /** * Address of endpoint */ - public final InetSocketAddress address() { + public InetSocketAddress getAddress() { return address; } /** * Time of last send in milliseconds or 0 for never */ - public final long lastSend() { + public long getLastSend() { return lastSend; } /** * Time of last receive in milliseconds or 0 for never */ - public final long lastReceive() { + public long getLastReceive() { return lastReceive; } - /** - * Is path fixed? (i.e. not learned, static) - */ - public final boolean isFixed() { - return fixed; - } - /** * Is path preferred? */ - public final boolean isPreferred() { + public 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 index fce183d9c..d69a1f1bb 100644 --- a/java/src/com/zerotier/sdk/PeerRole.java +++ b/java/src/com/zerotier/sdk/PeerRole.java @@ -27,19 +27,45 @@ package com.zerotier.sdk; +/** + * What trust hierarchy role does this peer have? + * + * Defined in ZeroTierOne.h as ZT_PeerRole + */ public enum PeerRole { + /** * An ordinary node */ - PEER_ROLE_LEAF, + PEER_ROLE_LEAF(0), /** * moon root */ - PEER_ROLE_MOON, + PEER_ROLE_MOON(1), /** * planetary root */ - PEER_ROLE_PLANET -} \ No newline at end of file + PEER_ROLE_PLANET(2); + + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private final int id; + + PeerRole(int id) { + this.id = id; + } + + public static PeerRole fromInt(int id) { + switch (id) { + case 0: + return PEER_ROLE_LEAF; + case 1: + return PEER_ROLE_MOON; + case 2: + return PEER_ROLE_PLANET; + default: + throw new RuntimeException("Unhandled value: " + id); + } + } +} diff --git a/java/src/com/zerotier/sdk/ResultCode.java b/java/src/com/zerotier/sdk/ResultCode.java index 09e7d3b13..dc8a901b5 100644 --- a/java/src/com/zerotier/sdk/ResultCode.java +++ b/java/src/com/zerotier/sdk/ResultCode.java @@ -34,12 +34,20 @@ package com.zerotier.sdk; * occurs, the node should be considered to not be working correctly. These * indicate serious problems like an inaccessible data store or a compile * problem.

+ * + * Defined in ZeroTierOne.h as ZT_ResultCode */ public enum ResultCode { + /** * Operation completed normally */ - RESULT_OK(0), + RESULT_OK(0), + + /** + * Call produced no error but no action was taken + */ + RESULT_OK_IGNORED(1), // Fatal errors (>=100, <1000) /** @@ -68,12 +76,36 @@ public enum ResultCode { RESULT_ERROR_BAD_PARAMETER(1002); - - private final int id; - ResultCode(int id) { this.id = id; } - public int getValue() { return id; } + private final int id; - public boolean isFatal(int id) { - return (id > 100 && id < 1000); + ResultCode(int id) { + this.id = id; } -} \ No newline at end of file + + public static ResultCode fromInt(int id) { + switch (id) { + case 0: + return RESULT_OK; + case 1: + return RESULT_OK_IGNORED; + case 100: + return RESULT_FATAL_ERROR_OUT_OF_MEMORY; + case 101: + return RESULT_FATAL_ERROR_DATA_STORE_FAILED; + case 102: + return RESULT_FATAL_ERROR_INTERNAL; + case 1000: + return RESULT_ERROR_NETWORK_NOT_FOUND; + case 1001: + return RESULT_ERROR_UNSUPPORTED_OPERATION; + case 1002: + return RESULT_ERROR_BAD_PARAMETER; + default: + throw new RuntimeException("Unhandled value: " + id); + } + } + + public boolean isFatal() { + return (id >= 100 && id < 1000); + } +} diff --git a/java/src/com/zerotier/sdk/Version.java b/java/src/com/zerotier/sdk/Version.java index c93c25970..0dbe1d2a5 100644 --- a/java/src/com/zerotier/sdk/Version.java +++ b/java/src/com/zerotier/sdk/Version.java @@ -27,10 +27,27 @@ package com.zerotier.sdk; -public final class Version { - private Version() {} - - public int major = 0; - public int minor = 0; - public int revision = 0; -} \ No newline at end of file +public class Version { + + private final int major; + private final int minor; + private final int revision; + + public Version(int major, int minor, int revision) { + this.major = major; + this.minor = minor; + this.revision = revision; + } + + public int getMajor() { + return major; + } + + public int getMinor() { + return minor; + } + + public int getRevision() { + return revision; + } +} diff --git a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java index c7b48d5c5..bcf64854a 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java @@ -29,197 +29,302 @@ package com.zerotier.sdk; import android.util.Log; -import java.lang.Comparable; -import java.lang.Override; -import java.lang.String; -import java.util.ArrayList; +import com.zerotier.sdk.util.StringUtils; + import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; -public final class VirtualNetworkConfig implements Comparable { +/** + * Virtual network configuration + * + * Defined in ZeroTierOne.h as ZT_VirtualNetworkConfig + */ +public class VirtualNetworkConfig implements Comparable { + private final static String TAG = "VirtualNetworkConfig"; public static final int MAX_MULTICAST_SUBSCRIPTIONS = 4096; public static final int ZT_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 InetSocketAddress[] assignedAddresses; - private VirtualNetworkRoute[] routes; - private VirtualNetworkDNS dns; + private final long nwid; - private VirtualNetworkConfig() { + private final long mac; + private final String name; + + private final VirtualNetworkStatus status; + + private final VirtualNetworkType type; + + private final int mtu; + + private final boolean dhcp; + + private final boolean bridge; + + private final boolean broadcastEnabled; + + private final int portError; + + private final long netconfRevision; + + private final InetSocketAddress[] assignedAddresses; + + private final VirtualNetworkRoute[] routes; + + private final VirtualNetworkDNS dns; + + public VirtualNetworkConfig(long nwid, long mac, String name, VirtualNetworkStatus status, VirtualNetworkType type, int mtu, boolean dhcp, boolean bridge, boolean broadcastEnabled, int portError, long netconfRevision, InetSocketAddress[] assignedAddresses, VirtualNetworkRoute[] routes, VirtualNetworkDNS dns) { + this.nwid = nwid; + this.mac = mac; + this.name = name; + this.status = status; + this.type = type; + if (mtu < 0) { + throw new RuntimeException("mtu < 0: " + mtu); + } + this.mtu = mtu; + this.dhcp = dhcp; + this.bridge = bridge; + this.broadcastEnabled = broadcastEnabled; + this.portError = portError; + if (netconfRevision < 0) { + throw new RuntimeException("netconfRevision < 0: " + netconfRevision); + } + this.netconfRevision = netconfRevision; + this.assignedAddresses = assignedAddresses; + this.routes = routes; + this.dns = dns; } - public boolean equals(VirtualNetworkConfig cfg) { - ArrayList aaCurrent = new ArrayList<>(); - ArrayList aaNew = new ArrayList<>(); - for (InetSocketAddress s : assignedAddresses) { - aaCurrent.add(s.toString()); - } - for (InetSocketAddress s : cfg.assignedAddresses) { - aaNew.add(s.toString()); - } - Collections.sort(aaCurrent); - Collections.sort(aaNew); - boolean aaEqual = aaCurrent.equals(aaNew); + @Override + public String toString() { + return "VirtualNetworkConfig(" + StringUtils.networkIdToString(nwid) + ", " + StringUtils.macAddressToString(mac) + ", " + name + ", " + status + ", " + type + ", " + mtu + ", " + dhcp + ", " + bridge + ", " + broadcastEnabled + ", " + portError + ", " + netconfRevision + ", " + Arrays.toString(assignedAddresses) + ", " + Arrays.toString(routes) + ", " + dns + ")"; + } - ArrayList rCurrent = new ArrayList<>(); - ArrayList rNew = new ArrayList<>(); - for (VirtualNetworkRoute r : routes) { - rCurrent.add(r.toString()); + @Override + public boolean equals(Object o) { + + if (!(o instanceof VirtualNetworkConfig)) { + return false; } - for (VirtualNetworkRoute r : cfg.routes) { - rNew.add(r.toString()); - } - Collections.sort(rCurrent); - Collections.sort(rNew); - boolean routesEqual = rCurrent.equals(rNew); + + VirtualNetworkConfig cfg = (VirtualNetworkConfig) o; if (this.nwid != cfg.nwid) { - Log.i(TAG, "nwid Changed. Old: " + Long.toHexString(this.nwid) + " (" + Long.toString(this.nwid) + "), " + - "New: " + Long.toHexString(cfg.nwid) + " (" + Long.toString(cfg.nwid) + ")"); + Log.i(TAG, "NetworkID Changed. Old: " + StringUtils.networkIdToString(this.nwid) + " (" + this.nwid + "), " + + "New: " + StringUtils.networkIdToString(cfg.nwid) + " (" + cfg.nwid + ")"); + + return false; } + if (this.mac != cfg.mac) { - Log.i(TAG, "MAC Changed. Old: " + Long.toHexString(this.mac) + ", New: " + Long.toHexString(cfg.mac)); + Log.i(TAG, "MAC Changed. Old: " + StringUtils.macAddressToString(this.mac) + ", New: " + StringUtils.macAddressToString(cfg.mac)); + + return false; } if (!this.name.equals(cfg.name)) { - Log.i(TAG, "Name Changed. Old: " + this.name + " New: "+ cfg.name); + Log.i(TAG, "Name Changed. Old: " + this.name + ", New: " + cfg.name); + + return false; } - if (!this.type.equals(cfg.type)) { - Log.i(TAG, "TYPE changed. Old " + this.type + ", New: " + cfg.type); + if (this.status != cfg.status) { + Log.i(TAG, "Status Changed. Old: " + this.status + ", New: " + cfg.status); + + return false; + } + + if (this.type != cfg.type) { + Log.i(TAG, "Type changed. Old " + this.type + ", New: " + cfg.type); + + return false; } if (this.mtu != cfg.mtu) { - Log.i(TAG, "MTU Changed. Old: " + this.mtu + ", New: " + cfg.mtu); + Log.i(TAG, "MTU Changed. Old: " + this.mtu + ", New: " + cfg.mtu); + + return false; } if (this.dhcp != cfg.dhcp) { Log.i(TAG, "DHCP Flag Changed. Old: " + this.dhcp + ", New: " + cfg.dhcp); + + return false; } if (this.bridge != cfg.bridge) { Log.i(TAG, "Bridge Flag Changed. Old: " + this.bridge + ", New: " + cfg.bridge); + + return false; } if (this.broadcastEnabled != cfg.broadcastEnabled) { - Log.i(TAG, "Broadcast Flag Changed. Old: "+ this.broadcastEnabled +", New: " + this.broadcastEnabled); + Log.i(TAG, "Broadcast Flag Changed. Old: "+ this.broadcastEnabled + ", New: " + cfg.broadcastEnabled); + + return false; } if (this.portError != cfg.portError) { - Log.i(TAG, "Port Error Changed. Old: " + this.portError + ", New: " + this.portError); + Log.i(TAG, "Port Error Changed. Old: " + this.portError + ", New: " + cfg.portError); + + return false; } - if (this.enabled != cfg.enabled) { - Log.i(TAG, "Enabled Changed. Old: " + this.enabled + ", New: " + this.enabled); + if (this.netconfRevision != cfg.netconfRevision) { + Log.i(TAG, "NetConfRevision Changed. Old: " + this.netconfRevision + ", New: " + cfg.netconfRevision); + + return false; } - if (!aaEqual) { + if (!Arrays.equals(assignedAddresses, cfg.assignedAddresses)) { + + ArrayList aaCurrent = new ArrayList<>(); + ArrayList aaNew = new ArrayList<>(); + for (InetSocketAddress s : assignedAddresses) { + aaCurrent.add(s.toString()); + } + for (InetSocketAddress s : cfg.assignedAddresses) { + aaNew.add(s.toString()); + } + Collections.sort(aaCurrent); + Collections.sort(aaNew); + Log.i(TAG, "Assigned Addresses Changed"); Log.i(TAG, "Old:"); for (String s : aaCurrent) { Log.i(TAG, " " + s); } + Log.i(TAG, ""); Log.i(TAG, "New:"); for (String s : aaNew) { Log.i(TAG, " " +s); } + Log.i(TAG, ""); + + return false; } - if (!routesEqual) { + if (!Arrays.equals(routes, cfg.routes)) { + + ArrayList rCurrent = new ArrayList<>(); + ArrayList rNew = new ArrayList<>(); + for (VirtualNetworkRoute r : routes) { + rCurrent.add(r.toString()); + } + for (VirtualNetworkRoute r : cfg.routes) { + rNew.add(r.toString()); + } + Collections.sort(rCurrent); + Collections.sort(rNew); + Log.i(TAG, "Managed Routes Changed"); Log.i(TAG, "Old:"); for (String s : rCurrent) { Log.i(TAG, " " + s); } + Log.i(TAG, ""); Log.i(TAG, "New:"); for (String s : rNew) { Log.i(TAG, " " + s); } + Log.i(TAG, ""); + + return false; } - boolean dnsEquals = false; - if (this.dns == null || cfg.dns == null) { - dnsEquals = true; - } else if (this.dns != null) { - dnsEquals = this.dns.equals(cfg.dns); + boolean dnsEquals; + if (this.dns == null) { + //noinspection RedundantIfStatement + if (cfg.dns == null) { + dnsEquals = true; + } else { + dnsEquals = false; + } + } else { + if (cfg.dns == null) { + dnsEquals = false; + } else { + dnsEquals = this.dns.equals(cfg.dns); + } } - return this.nwid == cfg.nwid && - this.mac == cfg.mac && - this.name.equals(cfg.name) && - this.status.equals(cfg.status) && - this.type.equals(cfg.type) && - this.mtu == cfg.mtu && - this.dhcp == cfg.dhcp && - this.bridge == cfg.bridge && - this.broadcastEnabled == cfg.broadcastEnabled && - this.portError == cfg.portError && - this.enabled == cfg.enabled && - dnsEquals && - aaEqual && routesEqual; + if (!dnsEquals) { + return false; + } + + return true; } + @Override public int compareTo(VirtualNetworkConfig cfg) { - if(cfg.nwid == this.nwid) { - return 0; - } else { - return this.nwid > cfg.nwid ? 1 : -1; - } + return Long.compare(this.nwid, cfg.nwid); + } + + @Override + public int hashCode() { + + int result = 17; + result = 37 * result + (int) (nwid ^ (nwid >>> 32)); + result = 37 * result + (int) (mac ^ (mac >>> 32)); + result = 37 * result + name.hashCode(); + result = 37 * result + status.hashCode(); + result = 37 * result + type.hashCode(); + result = 37 * result + mtu; + result = 37 * result + (dhcp ? 1 : 0); + result = 37 * result + (bridge ? 1 : 0); + result = 37 * result + (broadcastEnabled ? 1 : 0); + result = 37 * result + portError; + result = 37 * result + (int) (netconfRevision ^ (netconfRevision >>> 32)); + result = 37 * result + Arrays.hashCode(assignedAddresses); + result = 37 * result + Arrays.hashCode(routes); + result = 37 * result + (dns == null ? 0 : dns.hashCode()); + + return result; } /** * 64-bit ZeroTier network ID */ - public final long networkId() { + public long getNwid() { return nwid; } /** - * Ethernet MAC (40 bits) that should be assigned to port + * Ethernet MAC (48 bits) that should be assigned to port */ - public final long macAddress() { + public long getMac() { return mac; } /** * Network name (from network configuration master) */ - public final String name() { + public String getName() { return name; } /** * Network configuration request status */ - public final VirtualNetworkStatus networkStatus() { + public VirtualNetworkStatus getStatus() { return status; } /** * Network type */ - public final VirtualNetworkType networkType() { + public VirtualNetworkType getType() { return type; } /** * Maximum interface MTU */ - public final int mtu() { + public int getMtu() { return mtu; } @@ -230,7 +335,7 @@ public final class VirtualNetworkConfig implements Comparable */ - public final boolean isDhcpAvailable() { + public boolean isDhcp() { return dhcp; } @@ -240,21 +345,21 @@ public final class VirtualNetworkConfig implements ComparableThis is informational. If this is false, bridged packets will simply * be dropped and bridging won't work.

*/ - public final boolean isBridgeEnabled() { + public boolean isBridge() { return bridge; } /** * If true, this network supports and allows broadcast (ff:ff:ff:ff:ff:ff) traffic */ - public final boolean broadcastEnabled() { + public boolean isBroadcastEnabled() { 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() { + public int getPortError() { return portError; } @@ -263,12 +368,12 @@ public final class VirtualNetworkConfig implements ComparableIf this is zero, it means we're still waiting for our netconf.

*/ - public final long netconfRevision() { + public long getNetconfRevision() { return netconfRevision; } /** - * ZeroTier-assigned addresses (in {@link java.net.InetSocketAddress} objects) + * ZeroTier-assigned addresses (in {@link 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. @@ -277,16 +382,21 @@ public final class VirtualNetworkConfig implements Comparable * - * This should not call {@link Node#multicastSubscribe} or other network-modifying + * This should not call {@link Node#multicastSubscribe(long, long)} or other network-modifying * methods, as this could cause a deadlock in multithreaded or interrupt * driven environments. * @@ -53,8 +53,8 @@ public interface VirtualNetworkConfigListener { * @param config {@link VirtualNetworkConfig} object with the new configuration * @return 0 on success */ - public int onNetworkConfigurationUpdated( + 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 index b70eb4786..a1981bd15 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkConfigOperation.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkConfigOperation.java @@ -24,26 +24,55 @@ * redistribute it in a modified binary form, please contact ZeroTier Networks * LLC. Start here: http://www.zerotier.com/ */ + package com.zerotier.sdk; +/** + * Virtual network configuration update type + * + * Defined in ZeroTierOne.h as ZT_VirtualNetworkConfigOperation + */ public enum VirtualNetworkConfigOperation { + /** * Network is coming up (either for the first time or after service restart) */ - VIRTUAL_NETWORK_CONFIG_OPERATION_UP, + VIRTUAL_NETWORK_CONFIG_OPERATION_UP(1), /** * Network configuration has been updated */ - VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE, + VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE(2), /** * Network is going down (not permanently) */ - VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN, + VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN(3), /** * Network is going down permanently (leave/delete) */ - VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY + VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY(4); + + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private final int id; + + VirtualNetworkConfigOperation(int id) { + this.id = id; + } + + public static VirtualNetworkConfigOperation fromInt(int id) { + switch (id) { + case 1: + return VIRTUAL_NETWORK_CONFIG_OPERATION_UP; + case 2: + return VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE; + case 3: + return VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN; + case 4: + return VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY; + default: + throw new RuntimeException("Unhandled value: " + id); + } + } } diff --git a/java/src/com/zerotier/sdk/VirtualNetworkDNS.java b/java/src/com/zerotier/sdk/VirtualNetworkDNS.java index 7046fd424..6e4bb3d22 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkDNS.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkDNS.java @@ -8,15 +8,48 @@ package com.zerotier.sdk; import java.net.InetSocketAddress; import java.util.ArrayList; +/** + * DNS configuration to be pushed on a virtual network + * + * Defined in ZeroTierOne.h as ZT_VirtualNetworkDNS + */ public class VirtualNetworkDNS implements Comparable { - private String domain; - private ArrayList servers; - public VirtualNetworkDNS() {} + private final String domain; + private final ArrayList servers; - public boolean equals(VirtualNetworkDNS o) { - if (o == null) return false; - return domain.equals(o.domain) && servers.equals(o.servers); + public VirtualNetworkDNS(String domain, ArrayList servers) { + this.domain = domain; + this.servers = servers; + } + + @Override + public String toString() { + return "VirtualNetworkDNS(" + domain + ", " + servers + ")"; + } + + @Override + public boolean equals(Object o) { + + if (o == null) { + return false; + } + + if (!(o instanceof VirtualNetworkDNS)) { + return false; + } + + VirtualNetworkDNS d = (VirtualNetworkDNS) o; + + if (!domain.equals(d.domain)) { + return false; + } + + if (!servers.equals(d.servers)) { + return false; + } + + return true; } @Override @@ -24,7 +57,21 @@ public class VirtualNetworkDNS implements Comparable { return domain.compareTo(o.domain); } - public String getSearchDomain() { return domain; } + @Override + public int hashCode() { - public ArrayList getServers() { return servers; } + int result = 17; + result = 37 * result + domain.hashCode(); + result = 37 * result + servers.hashCode(); + + return result; + } + + public String getDomain() { + return domain; + } + + public ArrayList getServers() { + return servers; + } } diff --git a/java/src/com/zerotier/sdk/VirtualNetworkFrameListener.java b/java/src/com/zerotier/sdk/VirtualNetworkFrameListener.java index 9ad322825..650c9cedc 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkFrameListener.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkFrameListener.java @@ -28,17 +28,18 @@ 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 etherType EtherType + * @param vlanId VLAN ID * @param frameData data to send */ - public void onVirtualNetworkFrame( + void onVirtualNetworkFrame( long nwid, long srcMac, long destMac, diff --git a/java/src/com/zerotier/sdk/VirtualNetworkRoute.java b/java/src/com/zerotier/sdk/VirtualNetworkRoute.java index 8dd700c09..afd9ee45a 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkRoute.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkRoute.java @@ -29,80 +29,135 @@ package com.zerotier.sdk; import java.net.InetSocketAddress; -public final class VirtualNetworkRoute implements Comparable +/** + * A route to be pushed on a virtual network + * + * Defined in ZeroTierOne.h as ZT_VirtualNetworkRoute + */ +public class VirtualNetworkRoute implements Comparable { - private VirtualNetworkRoute() { - target = null; - via = null; - flags = 0; - metric = 0; - } - /** * Target network / netmask bits (in port field) or NULL or 0.0.0.0/0 for default */ - public InetSocketAddress target; - + private final InetSocketAddress target; + /** * Gateway IP address (port ignored) or NULL (family == 0) for LAN-local (no gateway) */ - public InetSocketAddress via; + private final InetSocketAddress via; /** * Route flags */ - public int flags; + private final int flags; /** * Route metric (not currently used) */ - public int metric; + private final int metric; - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(target.toString()); - if (via != null) { - sb.append(via.toString()); - } - return sb.toString(); + public VirtualNetworkRoute(InetSocketAddress target, InetSocketAddress via, int flags, int metric) { + this.target = target; + this.via = via; + this.flags = flags; + this.metric = metric; } @Override - public int compareTo(VirtualNetworkRoute other) { - return this.toString().compareTo(other.toString()); - } + public String toString() { + return "VirtualNetworkRoute(" + target + ", " + via + ", " + flags + ", " + metric + ")"; + } - public boolean equals(VirtualNetworkRoute other) { - boolean targetEquals = false; - if (target == null && other.target == null) { - targetEquals = true; - } - else if (target == null && other.target != null) { - targetEquals = false; - } - else if (target != null && other.target == null) { - targetEquals = false; - } - else { - targetEquals = target.toString().equals(other.target.toString()); + @Override + public int compareTo(VirtualNetworkRoute other) { + throw new RuntimeException("Unimplemented"); + } + + @Override + public boolean equals(Object o) { + + if (!(o instanceof VirtualNetworkRoute)) { + return false; } + VirtualNetworkRoute other = (VirtualNetworkRoute) o; + + boolean targetEquals; + if (target == null) { + //noinspection RedundantIfStatement + if (other.target == null) { + targetEquals = true; + } else { + targetEquals = false; + } + } else { + if (other.target == null) { + targetEquals = false; + } else { + targetEquals = target.equals(other.target); + } + } + + if (!targetEquals) { + return false; + } boolean viaEquals; - if (via == null && other.via == null) { - viaEquals = true; - } - else if (via == null && other.via != null) { - viaEquals = false; - } - else if (via != null && other.via == null) { - viaEquals = false; - } - else { - viaEquals = via.toString().equals(other.via.toString()); + if (via == null) { + //noinspection RedundantIfStatement + if (other.via == null) { + viaEquals = true; + } else { + viaEquals = false; + } + } else { + if (other.via == null) { + viaEquals = false; + } else { + viaEquals = via.equals(other.via); + } } - return viaEquals && targetEquals; + if (!viaEquals) { + return false; + } + + if (flags != other.flags) { + return false; + } + + if (metric != other.metric) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + + int result = 17; + result = 37 * result + (target == null ? 0 : target.hashCode()); + result = 37 * result + (via == null ? 0 : via.hashCode()); + result = 37 * result + flags; + result = 37 * result + metric; + + return result; + } + + public InetSocketAddress getTarget() { + return target; + } + + public InetSocketAddress getVia() { + return via; + } + + public int getFlags() { + return flags; + } + + public int getMetric() { + return metric; } } diff --git a/java/src/com/zerotier/sdk/VirtualNetworkStatus.java b/java/src/com/zerotier/sdk/VirtualNetworkStatus.java index 68e01bd61..8a32ba6ad 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkStatus.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkStatus.java @@ -24,41 +24,76 @@ * redistribute it in a modified binary form, please contact ZeroTier Networks * LLC. Start here: http://www.zerotier.com/ */ + package com.zerotier.sdk; +/** + * Virtual network status codes + * + * Defined in ZeroTierOne.h as ZT_VirtualNetworkStatus + */ public enum VirtualNetworkStatus { + /** * Waiting for network configuration (also means revision == 0) */ - NETWORK_STATUS_REQUESTING_CONFIGURATION, + NETWORK_STATUS_REQUESTING_CONFIGURATION(0), /** * Configuration received and we are authorized */ - NETWORK_STATUS_OK, - - /** - * Netconf master said SSO auth required. - */ - NETWORK_STATUS_AUTHENTICATION_REQUIRED, + NETWORK_STATUS_OK(1), /** * Netconf master told us 'nope' */ - NETWORK_STATUS_ACCESS_DENIED, + NETWORK_STATUS_ACCESS_DENIED(2), /** * Netconf master exists, but this virtual network does not */ - NETWORK_STATUS_NOT_FOUND, + NETWORK_STATUS_NOT_FOUND(3), /** * Initialization of network failed or other internal error */ - NETWORK_STATUS_PORT_ERROR, + NETWORK_STATUS_PORT_ERROR(4), /** * ZeroTier One version too old */ - NETWORK_STATUS_CLIENT_TOO_OLD + NETWORK_STATUS_CLIENT_TOO_OLD(5), + + /** + * External authentication is required (e.g. SSO) + */ + NETWORK_STATUS_AUTHENTICATION_REQUIRED(6); + + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private final int id; + + VirtualNetworkStatus(int id) { + this.id = id; + } + + public static VirtualNetworkStatus fromInt(int id) { + switch (id) { + case 0: + return NETWORK_STATUS_REQUESTING_CONFIGURATION; + case 1: + return NETWORK_STATUS_OK; + case 2: + return NETWORK_STATUS_ACCESS_DENIED; + case 3: + return NETWORK_STATUS_NOT_FOUND; + case 4: + return NETWORK_STATUS_PORT_ERROR; + case 5: + return NETWORK_STATUS_CLIENT_TOO_OLD; + case 6: + return NETWORK_STATUS_AUTHENTICATION_REQUIRED; + default: + throw new RuntimeException("Unhandled value: " + id); + } + } } diff --git a/java/src/com/zerotier/sdk/VirtualNetworkType.java b/java/src/com/zerotier/sdk/VirtualNetworkType.java index ab1f4e087..44be8864b 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkType.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkType.java @@ -24,16 +24,41 @@ * redistribute it in a modified binary form, please contact ZeroTier Networks * LLC. Start here: http://www.zerotier.com/ */ + package com.zerotier.sdk; +/** + * Virtual network type codes + * + * Defined in ZeroTierOne.h as ZT_VirtualNetworkType + */ public enum VirtualNetworkType { + /** * Private networks are authorized via certificates of membership */ - NETWORK_TYPE_PRIVATE, + NETWORK_TYPE_PRIVATE(0), /** * Public networks have no access control -- they'll always be AUTHORIZED */ - NETWORK_TYPE_PUBLIC + NETWORK_TYPE_PUBLIC(1); + + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private final int id; + + VirtualNetworkType(int id) { + this.id = id; + } + + public static VirtualNetworkType fromInt(int id) { + switch (id) { + case 0: + return NETWORK_TYPE_PRIVATE; + case 1: + return NETWORK_TYPE_PUBLIC; + default: + throw new RuntimeException("Unhandled value: " + id); + } + } } diff --git a/java/src/com/zerotier/sdk/util/StringUtils.java b/java/src/com/zerotier/sdk/util/StringUtils.java new file mode 100644 index 000000000..c7bcc5d9f --- /dev/null +++ b/java/src/com/zerotier/sdk/util/StringUtils.java @@ -0,0 +1,52 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2023 ZeroTier, Inc. https://www.zerotier.com/ + */ + +package com.zerotier.sdk.util; + +public class StringUtils { + + /** + * Convert mac address to string. + * + * @param mac MAC address + * @return string in XX:XX:XX:XX:XX:XX format + */ + public static String macAddressToString(long mac) { + + int[] macChars = new int[6]; + for (int i = 0; i < 6; i++) { + macChars[i] = (int) (mac % 256); + mac >>= 8; + } + + return String.format("%02x:%02x:%02x:%02x:%02x:%02x", macChars[5], macChars[4], macChars[3], macChars[2], macChars[1], macChars[0]); + } + + /** + * Convert long to hex string. + * + * @param networkId long + * @return string with 0 padding + */ + public static String networkIdToString(long networkId) { + return String.format("%016x", networkId); + } + + /** + * Convert node address to string. + * + * Node addresses are 40 bits, so print 10 hex characters. + * + * @param address Node address + * @return formatted string + */ + public static String addressToString(long address) { + return String.format("%010x", address); + } + + public static String etherTypeToString(long etherType) { + return String.format("%04x", etherType); + } +} diff --git a/java/test/com/zerotier/sdk/util/StringUtilsTest.java b/java/test/com/zerotier/sdk/util/StringUtilsTest.java new file mode 100644 index 000000000..257b14a99 --- /dev/null +++ b/java/test/com/zerotier/sdk/util/StringUtilsTest.java @@ -0,0 +1,73 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2023 ZeroTier, Inc. https://www.zerotier.com/ + */ + +package com.zerotier.sdk.util; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class StringUtilsTest { + + public StringUtilsTest() { + } + + public String oldMacDisplay(long mac) { + + String macStr = Long.toHexString(mac); + + if (macStr.length() > 12) { + throw new RuntimeException(); + } + + while (macStr.length() < 12) { + //noinspection StringConcatenationInLoop + macStr = "0" + macStr; + } + + //noinspection StringBufferReplaceableByString + StringBuilder displayMac = new StringBuilder(); + displayMac.append(macStr.charAt(0)); + displayMac.append(macStr.charAt(1)); + displayMac.append(':'); + displayMac.append(macStr.charAt(2)); + displayMac.append(macStr.charAt(3)); + displayMac.append(':'); + displayMac.append(macStr.charAt(4)); + displayMac.append(macStr.charAt(5)); + displayMac.append(':'); + displayMac.append(macStr.charAt(6)); + displayMac.append(macStr.charAt(7)); + displayMac.append(':'); + displayMac.append(macStr.charAt(8)); + displayMac.append(macStr.charAt(9)); + displayMac.append(':'); + displayMac.append(macStr.charAt(10)); + displayMac.append(macStr.charAt(11)); + + return displayMac.toString(); + } + + @Test + public void testMacDisplay() { + + long mac1 = 1234567891; + assertThat(StringUtils.macAddressToString(mac1)).isEqualTo(oldMacDisplay(mac1)); + + long mac2 = 999999999; + assertThat(StringUtils.macAddressToString(mac2)).isEqualTo(oldMacDisplay(mac2)); + + long mac3 = 0x7fffffffffffL; + assertThat(StringUtils.macAddressToString(mac3)).isEqualTo(oldMacDisplay(mac3)); + assertThat(StringUtils.macAddressToString(mac3)).isEqualTo("7f:ff:ff:ff:ff:ff"); + + long mac4 = 0x7fafcf3f8fffL; + assertThat(StringUtils.macAddressToString(mac4)).isEqualTo(oldMacDisplay(mac4)); + assertThat(StringUtils.macAddressToString(mac4)).isEqualTo("7f:af:cf:3f:8f:ff"); + } +} diff --git a/one.cpp b/one.cpp index 46a23b1ee..ba5be9b18 100644 --- a/one.cpp +++ b/one.cpp @@ -2235,6 +2235,27 @@ int main(int argc,char **argv) } } + // Check and fix permissions on critical files at startup + try { + char p[4096]; + OSUtils::ztsnprintf(p, sizeof(p), "%s" ZT_PATH_SEPARATOR_S "identity.secret", homeDir.c_str()); + if (OSUtils::fileExists(p)) { + OSUtils::lockDownFile(p, false); + } + } + catch (...) { + } + + try { + char p[4096]; + OSUtils::ztsnprintf(p, sizeof(p), "%s" ZT_PATH_SEPARATOR_S "authtoken.secret", homeDir.c_str()); + if (OSUtils::fileExists(p)) { + OSUtils::lockDownFile(p, false); + } + } + catch (...) { + } + // This can be removed once the new controller code has been around for many versions if (OSUtils::fileExists((homeDir + ZT_PATH_SEPARATOR_S + "controller.db").c_str(),true)) { fprintf(stderr,"%s: FATAL: an old controller.db exists in %s -- see instructions in controller/README.md for how to migrate!" ZT_EOL_S,argv[0],homeDir.c_str()); diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp index 36814523a..e237325c4 100644 --- a/osdep/OSUtils.cpp +++ b/osdep/OSUtils.cpp @@ -257,6 +257,16 @@ void OSUtils::lockDownFile(const char *path,bool isDir) CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); } + + // Remove 'Everyone' group from R/RX access + startupInfo.cb = sizeof(startupInfo); + memset(&startupInfo, 0, sizeof(STARTUPINFOA)); + memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); + if (CreateProcessA(NULL, (LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /remove:g Everyone /t /c /Q").c_str(), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startupInfo, &processInfo)) { + WaitForSingleObject(processInfo.hProcess, INFINITE); + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + } } #endif #endif