summaryrefslogtreecommitdiff
path: root/zerotierone/java/jni/com_zerotierone_sdk_Node.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zerotierone/java/jni/com_zerotierone_sdk_Node.cpp')
-rw-r--r--zerotierone/java/jni/com_zerotierone_sdk_Node.cpp1370
1 files changed, 1370 insertions, 0 deletions
diff --git a/zerotierone/java/jni/com_zerotierone_sdk_Node.cpp b/zerotierone/java/jni/com_zerotierone_sdk_Node.cpp
new file mode 100644
index 0000000..dbabf80
--- /dev/null
+++ b/zerotierone/java/jni/com_zerotierone_sdk_Node.cpp
@@ -0,0 +1,1370 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "com_zerotierone_sdk_Node.h"
+#include "ZT_jniutils.h"
+#include "ZT_jnilookup.h"
+
+#include <ZeroTierOne.h>
+#include "Mutex.hpp"
+
+#include <map>
+#include <string>
+#include <assert.h>
+#include <string.h>
+
+// global static JNI Lookup Object
+JniLookup lookup;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+namespace {
+ struct JniRef
+ {
+ JniRef()
+ : jvm(NULL)
+ , node(NULL)
+ , dataStoreGetListener(NULL)
+ , dataStorePutListener(NULL)
+ , packetSender(NULL)
+ , eventListener(NULL)
+ , frameListener(NULL)
+ , configListener(NULL)
+ {}
+
+ ~JniRef()
+ {
+ JNIEnv *env = NULL;
+ jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+ env->DeleteGlobalRef(dataStoreGetListener);
+ env->DeleteGlobalRef(dataStorePutListener);
+ env->DeleteGlobalRef(packetSender);
+ env->DeleteGlobalRef(eventListener);
+ env->DeleteGlobalRef(frameListener);
+ env->DeleteGlobalRef(configListener);
+ }
+
+ uint64_t id;
+
+ JavaVM *jvm;
+
+ ZT_Node *node;
+
+ jobject dataStoreGetListener;
+ jobject dataStorePutListener;
+ jobject packetSender;
+ jobject eventListener;
+ jobject frameListener;
+ jobject configListener;
+ };
+
+
+ int VirtualNetworkConfigFunctionCallback(
+ ZT_Node *node,
+ void *userData,
+ uint64_t nwid,
+ void **,
+ enum ZT_VirtualNetworkConfigOperation operation,
+ const ZT_VirtualNetworkConfig *config)
+ {
+ LOGV("VritualNetworkConfigFunctionCallback");
+ JniRef *ref = (JniRef*)userData;
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+ jclass configListenerClass = env->GetObjectClass(ref->configListener);
+ if(configListenerClass == NULL)
+ {
+ LOGE("Couldn't find class for VirtualNetworkConfigListener instance");
+ return -1;
+ }
+
+ jmethodID configListenerCallbackMethod = lookup.findMethod(configListenerClass,
+ "onNetworkConfigurationUpdated",
+ "(JLcom/zerotier/sdk/VirtualNetworkConfigOperation;Lcom/zerotier/sdk/VirtualNetworkConfig;)I");
+ if(configListenerCallbackMethod == NULL)
+ {
+ LOGE("Couldn't find onVirtualNetworkFrame() method");
+ return -2;
+ }
+
+ jobject operationObject = createVirtualNetworkConfigOperation(env, operation);
+ if(operationObject == NULL)
+ {
+ LOGE("Error creating VirtualNetworkConfigOperation object");
+ return -3;
+ }
+
+ jobject networkConfigObject = newNetworkConfig(env, *config);
+ if(networkConfigObject == NULL)
+ {
+ LOGE("Error creating VirtualNetworkConfig object");
+ return -4;
+ }
+
+ return env->CallIntMethod(
+ ref->configListener,
+ configListenerCallbackMethod,
+ (jlong)nwid, operationObject, networkConfigObject);
+ }
+
+ void VirtualNetworkFrameFunctionCallback(ZT_Node *node,
+ void *userData,
+ uint64_t nwid,
+ void**,
+ uint64_t sourceMac,
+ uint64_t destMac,
+ unsigned int etherType,
+ unsigned int vlanid,
+ const void *frameData,
+ unsigned int frameLength)
+ {
+ LOGV("VirtualNetworkFrameFunctionCallback");
+ unsigned char* local = (unsigned char*)frameData;
+ LOGV("Type Bytes: 0x%02x%02x", local[12], local[13]);
+ JniRef *ref = (JniRef*)userData;
+ assert(ref->node == node);
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+
+ jclass frameListenerClass = env->GetObjectClass(ref->frameListener);
+ if(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);
+ 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");
+ return;
+ }
+
+ env->CallVoidMethod(ref->frameListener, frameListenerCallbackMethod, (jlong)nwid, (jlong)sourceMac, (jlong)destMac, (jlong)etherType, (jlong)vlanid, dataArray);
+ }
+
+
+ void EventCallback(ZT_Node *node,
+ void *userData,
+ enum ZT_Event event,
+ const void *data)
+ {
+ LOGV("EventCallback");
+ JniRef *ref = (JniRef*)userData;
+ 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);
+
+
+ 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)
+ {
+ return;
+ }
+
+ switch(event)
+ {
+ case ZT_EVENT_UP:
+ {
+ LOGD("Event Up");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_OFFLINE:
+ {
+ LOGD("Event Offline");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_ONLINE:
+ {
+ LOGD("Event Online");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_DOWN:
+ {
+ LOGD("Event Down");
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ break;
+ }
+ case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION:
+ {
+ LOGV("Identity Collision");
+ // call onEvent()
+ env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject);
+ }
+ 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);
+ }
+ }
+ break;
+ }
+ }
+
+ long DataStoreGetFunction(ZT_Node *node,
+ void *userData,
+ const char *objectName,
+ void *buffer,
+ unsigned long bufferSize,
+ unsigned long bufferIndex,
+ unsigned long *out_objectSize)
+ {
+ JniRef *ref = (JniRef*)userData;
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+ jclass dataStoreGetClass = env->GetObjectClass(ref->dataStoreGetListener);
+ if(dataStoreGetClass == NULL)
+ {
+ LOGE("Couldn't find class for DataStoreGetListener instance");
+ return -2;
+ }
+
+ jmethodID dataStoreGetCallbackMethod = lookup.findMethod(
+ dataStoreGetClass,
+ "onDataStoreGet",
+ "(Ljava/lang/String;[BJ[J)J");
+ if(dataStoreGetCallbackMethod == NULL)
+ {
+ LOGE("Couldn't find onDataStoreGet method");
+ return -2;
+ }
+
+ jstring nameStr = env->NewStringUTF(objectName);
+ if(nameStr == NULL)
+ {
+ LOGE("Error creating name string object");
+ return -2; // out of memory
+ }
+
+ jbyteArray bufferObj = env->NewByteArray(bufferSize);
+ if(bufferObj == NULL)
+ {
+ LOGE("Error creating byte[] buffer of size: %lu", bufferSize);
+ return -2;
+ }
+
+ jlongArray objectSizeObj = env->NewLongArray(1);
+ if(objectSizeObj == NULL)
+ {
+ LOGE("Error creating long[1] array for actual object size");
+ return -2; // couldn't create long[1] array
+ }
+
+ LOGV("Calling onDataStoreGet(%s, %p, %lu, %p)",
+ objectName, buffer, bufferIndex, objectSizeObj);
+
+ long retval = (long)env->CallLongMethod(
+ ref->dataStoreGetListener, dataStoreGetCallbackMethod,
+ nameStr, bufferObj, (jlong)bufferIndex, objectSizeObj);
+
+ if(retval > 0)
+ {
+ void *data = env->GetPrimitiveArrayCritical(bufferObj, NULL);
+ memcpy(buffer, data, retval);
+ env->ReleasePrimitiveArrayCritical(bufferObj, data, 0);
+
+ jlong *objSize = (jlong*)env->GetPrimitiveArrayCritical(objectSizeObj, NULL);
+ *out_objectSize = (unsigned long)objSize[0];
+ env->ReleasePrimitiveArrayCritical(objectSizeObj, objSize, 0);
+ }
+
+ LOGV("Out Object Size: %lu", *out_objectSize);
+
+ return retval;
+ }
+
+ int DataStorePutFunction(ZT_Node *node,
+ void *userData,
+ const char *objectName,
+ const void *buffer,
+ unsigned long bufferSize,
+ int secure)
+ {
+ JniRef *ref = (JniRef*)userData;
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+
+ jclass dataStorePutClass = env->GetObjectClass(ref->dataStorePutListener);
+ if(dataStorePutClass == NULL)
+ {
+ LOGE("Couldn't find class for DataStorePutListener instance");
+ return -1;
+ }
+
+ jmethodID dataStorePutCallbackMethod = lookup.findMethod(
+ dataStorePutClass,
+ "onDataStorePut",
+ "(Ljava/lang/String;[BZ)I");
+ if(dataStorePutCallbackMethod == NULL)
+ {
+ LOGE("Couldn't find onDataStorePut method");
+ return -2;
+ }
+
+ jmethodID deleteMethod = lookup.findMethod(dataStorePutClass,
+ "onDelete", "(Ljava/lang/String;)I");
+ if(deleteMethod == NULL)
+ {
+ LOGE("Couldn't find onDelete method");
+ return -3;
+ }
+
+ jstring nameStr = env->NewStringUTF(objectName);
+
+ if(buffer == NULL)
+ {
+ LOGD("JNI: Delete file: %s", objectName);
+ // delete operation
+ return env->CallIntMethod(
+ ref->dataStorePutListener, deleteMethod, nameStr);
+ }
+ else
+ {
+ LOGD("JNI: Write file: %s", objectName);
+ // set operation
+ jbyteArray bufferObj = env->NewByteArray(bufferSize);
+ if(env->ExceptionCheck() || bufferObj == NULL)
+ {
+ LOGE("Error creating byte array buffer!");
+ return -4;
+ }
+
+ env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer);
+ bool bsecure = secure != 0;
+
+ return env->CallIntMethod(ref->dataStorePutListener,
+ dataStorePutCallbackMethod,
+ nameStr, bufferObj, bsecure);
+ }
+ }
+
+ int WirePacketSendFunction(ZT_Node *node,
+ void *userData,
+ const struct sockaddr_storage *localAddress,
+ const struct sockaddr_storage *remoteAddress,
+ const void *buffer,
+ unsigned int bufferSize,
+ unsigned int ttl)
+ {
+ LOGV("WirePacketSendFunction(%p, %p, %p, %d)", localAddress, remoteAddress, buffer, bufferSize);
+ JniRef *ref = (JniRef*)userData;
+ assert(ref->node == node);
+
+ JNIEnv *env = NULL;
+ ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+
+ jclass packetSenderClass = env->GetObjectClass(ref->packetSender);
+ if(packetSenderClass == NULL)
+ {
+ LOGE("Couldn't find class for PacketSender instance");
+ return -1;
+ }
+
+ jmethodID packetSenderCallbackMethod = lookup.findMethod(packetSenderClass,
+ "onSendPacketRequested", "(Ljava/net/InetSocketAddress;Ljava/net/InetSocketAddress;[BI)I");
+ if(packetSenderCallbackMethod == NULL)
+ {
+ LOGE("Couldn't find onSendPacketRequested method");
+ return -2;
+ }
+
+ jobject localAddressObj = NULL;
+ if(memcmp(localAddress, &ZT_SOCKADDR_NULL, sizeof(sockaddr_storage)) != 0)
+ {
+ localAddressObj = newInetSocketAddress(env, *localAddress);
+ }
+
+ jobject remoteAddressObj = newInetSocketAddress(env, *remoteAddress);
+ jbyteArray bufferObj = env->NewByteArray(bufferSize);
+ env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer);
+ int retval = env->CallIntMethod(ref->packetSender, packetSenderCallbackMethod, localAddressObj, remoteAddressObj, bufferObj);
+
+ LOGV("JNI Packet Sender returned: %d", retval);
+ return retval;
+ }
+
+ typedef std::map<uint64_t, JniRef*> NodeMap;
+ static NodeMap nodeMap;
+ ZeroTier::Mutex nodeMapMutex;
+
+ ZT_Node* findNode(uint64_t nodeId)
+ {
+ ZeroTier::Mutex::Lock lock(nodeMapMutex);
+ NodeMap::iterator found = nodeMap.find(nodeId);
+ if(found != nodeMap.end())
+ {
+ JniRef *ref = found->second;
+ return ref->node;
+ }
+ return NULL;
+ }
+}
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
+{
+ lookup.setJavaVM(vm);
+ return JNI_VERSION_1_6;
+}
+
+JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved)
+{
+
+}
+
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: node_init
+ * Signature: (J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init(
+ JNIEnv *env, jobject obj, jlong now)
+{
+ LOGV("Creating ZT_Node struct");
+ jobject resultObject = createResultObject(env, ZT_RESULT_OK);
+
+ ZT_Node *node;
+ JniRef *ref = new JniRef;
+ ref->id = (uint64_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);
+
+ ZT_ResultCode rc = ZT_Node_new(
+ &node,
+ ref,
+ (uint64_t)now,
+ &DataStoreGetFunction,
+ &DataStorePutFunction,
+ &WirePacketSendFunction,
+ &VirtualNetworkFrameFunctionCallback,
+ &VirtualNetworkConfigFunctionCallback,
+ NULL,
+ &EventCallback);
+
+ if(rc != ZT_RESULT_OK)
+ {
+ LOGE("Error creating Node: %d", rc);
+ resultObject = createResultObject(env, rc);
+ if(node)
+ {
+ ZT_Node_delete(node);
+ node = NULL;
+ }
+ delete ref;
+ ref = NULL;
+ return resultObject;
+ }
+
+ ZeroTier::Mutex::Lock lock(nodeMapMutex);
+ ref->node = node;
+ nodeMap.insert(std::make_pair(ref->id, ref));
+
+
+ return resultObject;
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: node_delete
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete(
+ JNIEnv *env, jobject obj, jlong id)
+{
+ LOGV("Destroying ZT_Node struct");
+ uint64_t nodeId = (uint64_t)id;
+
+ NodeMap::iterator found;
+ {
+ ZeroTier::Mutex::Lock lock(nodeMapMutex);
+ found = nodeMap.find(nodeId);
+ }
+
+ if(found != nodeMap.end())
+ {
+ JniRef *ref = found->second;
+ nodeMap.erase(found);
+
+ ZT_Node_delete(ref->node);
+
+ delete ref;
+ ref = NULL;
+ }
+ else
+ {
+ LOGE("Attempted to delete a node that doesn't exist!");
+ }
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: processVirtualNetworkFrame
+ * Signature: (JJJJJII[B[J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_now,
+ jlong in_nwid,
+ jlong in_sourceMac,
+ jlong in_destMac,
+ jint in_etherType,
+ jint in_vlanId,
+ jbyteArray in_frameData,
+ jlongArray out_nextBackgroundTaskDeadline)
+{
+ uint64_t nodeId = (uint64_t) id;
+
+ 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);
+ }
+
+ uint64_t now = (uint64_t)in_now;
+ uint64_t nwid = (uint64_t)in_nwid;
+ uint64_t sourceMac = (uint64_t)in_sourceMac;
+ uint64_t destMac = (uint64_t)in_destMac;
+ unsigned int etherType = (unsigned int)in_etherType;
+ unsigned int vlanId = (unsigned int)in_vlanId;
+
+ unsigned int frameLength = env->GetArrayLength(in_frameData);
+ void *frameData = env->GetPrimitiveArrayCritical(in_frameData, NULL);
+ void *localData = malloc(frameLength);
+ memcpy(localData, frameData, frameLength);
+ env->ReleasePrimitiveArrayCritical(in_frameData, frameData, 0);
+
+ uint64_t nextBackgroundTaskDeadline = 0;
+
+ ZT_ResultCode rc = ZT_Node_processVirtualNetworkFrame(
+ node,
+ now,
+ nwid,
+ sourceMac,
+ destMac,
+ etherType,
+ vlanId,
+ (const void*)localData,
+ frameLength,
+ &nextBackgroundTaskDeadline);
+
+ jlong *outDeadline = (jlong*)env->GetPrimitiveArrayCritical(out_nextBackgroundTaskDeadline, NULL);
+ outDeadline[0] = (jlong)nextBackgroundTaskDeadline;
+ env->ReleasePrimitiveArrayCritical(out_nextBackgroundTaskDeadline, outDeadline, 0);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: processWirePacket
+ * Signature: (JJLjava/net/InetSocketAddress;I[B[J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_now,
+ jobject in_localAddress,
+ jobject in_remoteAddress,
+ jbyteArray in_packetData,
+ jlongArray out_nextBackgroundTaskDeadline)
+{
+ uint64_t nodeId = (uint64_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 = env->GetArrayLength(out_nextBackgroundTaskDeadline);
+ if(nbtd_len < 1)
+ {
+ LOGE("nbtd_len < 1");
+ return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
+ }
+
+ uint64_t now = (uint64_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);
+ }
+
+ jmethodID getAddressMethod = lookup.findMethod(
+ inetAddressClass, "getAddress", "()[B");
+ if(getAddressMethod == NULL)
+ {
+ // cant find InetAddress.getAddres()
+ 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 localAddrObj = NULL;
+ if(in_localAddress != NULL)
+ {
+ localAddrObj = env->CallObjectMethod(in_localAddress, inetSockGetAddressMethod);
+ }
+
+ 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);
+
+
+ sockaddr_storage localAddress = {};
+
+ if(localAddrObj == NULL)
+ {
+ localAddress = ZT_SOCKADDR_NULL;
+ }
+ else
+ {
+ int localPort = env->CallIntMethod(in_localAddress, inetSock_getPort);
+ jbyteArray localAddressArray = (jbyteArray)env->CallObjectMethod(localAddrObj, getAddressMethod);
+ if(localAddressArray != NULL)
+ {
+
+ unsigned int localAddrSize = env->GetArrayLength(localAddressArray);
+ jbyte *addr = (jbyte*)env->GetPrimitiveArrayCritical(localAddressArray, NULL);
+
+ if(localAddrSize == 16)
+ {
+ sockaddr_in6 ipv6 = {};
+ ipv6.sin6_family = AF_INET6;
+ ipv6.sin6_port = htons(localPort);
+ memcpy(ipv6.sin6_addr.s6_addr, addr, 16);
+ memcpy(&localAddress, &ipv6, sizeof(sockaddr_in6));
+ }
+ else if(localAddrSize)
+ {
+ // IPV4 address
+ sockaddr_in ipv4 = {};
+ ipv4.sin_family = AF_INET;
+ ipv4.sin_port = htons(localPort);
+ memcpy(&ipv4.sin_addr, addr, 4);
+ memcpy(&localAddress, &ipv4, sizeof(sockaddr_in));
+ }
+ else
+ {
+ localAddress = ZT_SOCKADDR_NULL;
+ }
+ env->ReleasePrimitiveArrayCritical(localAddressArray, addr, 0);
+ }
+ }
+
+ // 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 = env->GetArrayLength(in_packetData);
+ if(packetLength == 0)
+ {
+ LOGE("Empty packet?!?");
+ return createResultObject(env, ZT_RESULT_FATAL_ERROR_INTERNAL);
+ }
+ void *packetData = env->GetPrimitiveArrayCritical(in_packetData, NULL);
+ void *localData = malloc(packetLength);
+ memcpy(localData, packetData, packetLength);
+ env->ReleasePrimitiveArrayCritical(in_packetData, packetData, 0);
+
+ uint64_t nextBackgroundTaskDeadline = 0;
+
+ ZT_ResultCode rc = ZT_Node_processWirePacket(
+ node,
+ now,
+ &localAddress,
+ &remoteAddress,
+ localData,
+ packetLength,
+ &nextBackgroundTaskDeadline);
+ if(rc != ZT_RESULT_OK)
+ {
+ LOGE("ZT_Node_processWirePacket returned: %d", rc);
+ }
+
+ free(localData);
+
+ jlong *outDeadline = (jlong*)env->GetPrimitiveArrayCritical(out_nextBackgroundTaskDeadline, NULL);
+ outDeadline[0] = (jlong)nextBackgroundTaskDeadline;
+ env->ReleasePrimitiveArrayCritical(out_nextBackgroundTaskDeadline, outDeadline, 0);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: processBackgroundTasks
+ * Signature: (JJ[J)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_now,
+ jlongArray out_nextBackgroundTaskDeadline)
+{
+ uint64_t nodeId = (uint64_t) id;
+ 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);
+ }
+
+ uint64_t now = (uint64_t)in_now;
+ uint64_t nextBackgroundTaskDeadline = 0;
+
+ ZT_ResultCode rc = ZT_Node_processBackgroundTasks(node, now, &nextBackgroundTaskDeadline);
+
+ jlong *outDeadline = (jlong*)env->GetPrimitiveArrayCritical(out_nextBackgroundTaskDeadline, NULL);
+ outDeadline[0] = (jlong)nextBackgroundTaskDeadline;
+ env->ReleasePrimitiveArrayCritical(out_nextBackgroundTaskDeadline, outDeadline, 0);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: join
+ * Signature: (JJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_join(
+ JNIEnv *env, jobject obj, jlong id, jlong in_nwid)
+{
+ uint64_t nodeId = (uint64_t) id;
+ 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);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: leave
+ * Signature: (JJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave(
+ JNIEnv *env, jobject obj, jlong id, jlong in_nwid)
+{
+ uint64_t nodeId = (uint64_t) id;
+ 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);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: multicastSubscribe
+ * Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_nwid,
+ jlong in_multicastGroup,
+ jlong in_multicastAdi)
+{
+ uint64_t nodeId = (uint64_t) id;
+ 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;
+ unsigned long multicastAdi = (unsigned long)in_multicastAdi;
+
+ ZT_ResultCode rc = ZT_Node_multicastSubscribe(
+ node, nwid, multicastGroup, multicastAdi);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: multicastUnsubscribe
+ * Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe(
+ JNIEnv *env, jobject obj,
+ jlong id,
+ jlong in_nwid,
+ jlong in_multicastGroup,
+ jlong in_multicastAdi)
+{
+ uint64_t nodeId = (uint64_t) id;
+ 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;
+ unsigned long multicastAdi = (unsigned long)in_multicastAdi;
+
+ ZT_ResultCode rc = ZT_Node_multicastUnsubscribe(
+ node, nwid, multicastGroup, multicastAdi);
+
+ return createResultObject(env, rc);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: address
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_com_zerotier_sdk_Node_address(
+ JNIEnv *env , jobject obj, jlong id)
+{
+ uint64_t nodeId = (uint64_t) id;
+ 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;
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: status
+ * Signature: (J)Lcom/zerotier/sdk/NodeStatus;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status
+ (JNIEnv *env, jobject obj, jlong id)
+{
+ uint64_t nodeId = (uint64_t) id;
+ 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, "<init>", "()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;
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: networkConfig
+ * Signature: (J)Lcom/zerotier/sdk/VirtualNetworkConfig;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networkConfig(
+ JNIEnv *env, jobject obj, jlong id, jlong nwid)
+{
+ uint64_t nodeId = (uint64_t) id;
+ 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);
+
+ jobject vnetConfigObject = newNetworkConfig(env, *vnetConfig);
+
+ ZT_Node_freeQueryResult(node, vnetConfig);
+
+ return vnetConfigObject;
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: version
+ * Signature: (J)Lcom/zerotier/sdk/Version;
+ */
+JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_version(
+ JNIEnv *env, jobject obj)
+{
+ int major = 0;
+ int minor = 0;
+ int revision = 0;
+ unsigned long featureFlags = 0;
+
+ ZT_version(&major, &minor, &revision, &featureFlags);
+
+ return newVersion(env, major, minor, revision, featureFlags);
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: peers
+ * Signature: (J)[Lcom/zerotier/sdk/Peer;
+ */
+JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers(
+ JNIEnv *env, jobject obj, jlong id)
+{
+ uint64_t nodeId = (uint64_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);
+
+ if(peerList == NULL)
+ {
+ LOGE("ZT_Node_peers returned NULL");
+ 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;
+ }
+ }
+
+ ZT_Node_freeQueryResult(node, peerList);
+ peerList = NULL;
+
+ return peerArrayObj;
+}
+
+/*
+ * Class: com_zerotier_sdk_Node
+ * Method: networks
+ * Signature: (J)[Lcom/zerotier/sdk/VirtualNetworkConfig;
+ */
+JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_networks(
+ JNIEnv *env, jobject obj, jlong id)
+{
+ uint64_t nodeId = (uint64_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)
+ {
+ 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;
+ }
+ }
+
+ ZT_Node_freeQueryResult(node, networkList);
+
+ return networkListObject;
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif \ No newline at end of file