From 8fb9df97511532a28ac8225282318dd1f31be0e4 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 3 Jun 2015 18:35:38 -0700 Subject: [PATCH 01/27] delete dead test code --- .../com/zerotier/one/AndroidFileProvider.java | 43 ---- java/src/com/zerotier/one/DataStore.java | 73 ------- .../zerotier/one/DataStoreFileProvider.java | 12 -- .../com/zerotier/one/JavaFileProvider.java | 46 ---- java/src/com/zerotier/one/OneService.java | 204 ------------------ 5 files changed, 378 deletions(-) delete mode 100644 java/src/com/zerotier/one/AndroidFileProvider.java delete mode 100644 java/src/com/zerotier/one/DataStore.java delete mode 100644 java/src/com/zerotier/one/DataStoreFileProvider.java delete mode 100644 java/src/com/zerotier/one/JavaFileProvider.java delete mode 100644 java/src/com/zerotier/one/OneService.java diff --git a/java/src/com/zerotier/one/AndroidFileProvider.java b/java/src/com/zerotier/one/AndroidFileProvider.java deleted file mode 100644 index 0988f9dfa..000000000 --- a/java/src/com/zerotier/one/AndroidFileProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.zerotier.one; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; - -import android.content.Context; -import android.util.Log; - -public class AndroidFileProvider implements DataStoreFileProvider { - private static final String TAG = "AndroidFileProvider"; - - Context _ctx; - - public AndroidFileProvider(Context ctx) { - this._ctx = ctx; - } - - @Override - public FileInputStream getInputFileStream(String name) - throws FileNotFoundException { - Log.d(TAG, "Returning FileInputStream for: " + name); - return _ctx.openFileInput(name); - } - - @Override - public FileOutputStream getOutputFileStream(String name) - throws FileNotFoundException { - Log.d(TAG, "Returning FileOutputStream for: " + name); - return _ctx.openFileOutput(name, Context.MODE_PRIVATE); - } - - @Override - public void deleteFile(String name) throws IOException { - boolean success = _ctx.deleteFile(name); - if(!success) - { - throw new IOException("Unable to delete file."); - } - } - -} diff --git a/java/src/com/zerotier/one/DataStore.java b/java/src/com/zerotier/one/DataStore.java deleted file mode 100644 index d15b2d3d2..000000000 --- a/java/src/com/zerotier/one/DataStore.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.zerotier.one; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; - -import com.zerotier.sdk.DataStoreGetListener; -import com.zerotier.sdk.DataStorePutListener; - -public class DataStore implements DataStoreGetListener, DataStorePutListener { - - private DataStoreFileProvider _provider; - - public DataStore(DataStoreFileProvider provider) { - this._provider = provider; - } - - @Override - public int onDataStorePut(String name, byte[] buffer, boolean secure) { - System.out.println("Writing File: " + name); - try { - FileOutputStream fos = _provider.getOutputFileStream(name); - fos.write(buffer); - fos.close(); - return 0; - } catch (FileNotFoundException fnf) { - fnf.printStackTrace(); - return -1; - } catch (IOException io) { - io.printStackTrace(); - return -2; - } - } - - @Override - public int onDelete(String name) { - System.out.println("Deleting File: " + name); - try { - _provider.deleteFile(name); - return 0; - } catch (IOException ex) { - ex.printStackTrace(); - return -1; - } - } - - @Override - public long onDataStoreGet(String name, byte[] out_buffer, - long bufferIndex, long[] out_objectSize) { - System.out.println("Reading File: " + name); - try { - FileInputStream fin = _provider.getInputFileStream(name); - out_objectSize[0] = fin.getChannel().size(); - if(bufferIndex > 0) - { - fin.skip(bufferIndex); - } - int read = fin.read(out_buffer); - fin.close(); - return read; - } catch (FileNotFoundException fnf) { - // Can't read a file that doesn't exist! - out_objectSize[0] = 0; - return 0; - } catch (IOException io) { - io.printStackTrace(); - return -2; - } - } - - -} diff --git a/java/src/com/zerotier/one/DataStoreFileProvider.java b/java/src/com/zerotier/one/DataStoreFileProvider.java deleted file mode 100644 index ffe078eb4..000000000 --- a/java/src/com/zerotier/one/DataStoreFileProvider.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.zerotier.one; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; - -public interface DataStoreFileProvider { - FileInputStream getInputFileStream(String name) throws FileNotFoundException; - FileOutputStream getOutputFileStream(String name) throws FileNotFoundException; - void deleteFile(String name) throws IOException; -} diff --git a/java/src/com/zerotier/one/JavaFileProvider.java b/java/src/com/zerotier/one/JavaFileProvider.java deleted file mode 100644 index 41889e2f1..000000000 --- a/java/src/com/zerotier/one/JavaFileProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.zerotier.one; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; - -public class JavaFileProvider implements DataStoreFileProvider { - private String _path; - - public JavaFileProvider(String path) { - this._path = path; - } - - @Override - public FileInputStream getInputFileStream(String name) - throws FileNotFoundException { - File f = new File(_path + File.separator + name); - return new FileInputStream(f); - } - - @Override - public FileOutputStream getOutputFileStream(String name) - throws FileNotFoundException { - File f = new File(_path + File.separator + name); - if(!f.exists()) - { - try { - f.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return new FileOutputStream(f); - } - - @Override - public void deleteFile(String name) throws IOException { - File f = new File(_path + File.separator + name); - boolean success = f.delete(); - if(!success) { - throw new IOException("Unable to delete file: " + _path + File.pathSeparator + name); - } - } -} diff --git a/java/src/com/zerotier/one/OneService.java b/java/src/com/zerotier/one/OneService.java deleted file mode 100644 index 1d3e34c85..000000000 --- a/java/src/com/zerotier/one/OneService.java +++ /dev/null @@ -1,204 +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/ - */ - - -package com.zerotier.one; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.SocketException; -import java.net.SocketTimeoutException; - -import com.zerotier.sdk.Event; -import com.zerotier.sdk.EventListener; -import com.zerotier.sdk.Node; -import com.zerotier.sdk.PacketSender; -import com.zerotier.sdk.ResultCode; -import com.zerotier.sdk.Version; -import com.zerotier.sdk.VirtualNetworkConfig; -import com.zerotier.sdk.VirtualNetworkConfigListener; -import com.zerotier.sdk.VirtualNetworkConfigOperation; -import com.zerotier.sdk.VirtualNetworkFrameListener; - -public class OneService extends Thread implements Runnable, PacketSender, - EventListener, VirtualNetworkConfigListener, - VirtualNetworkFrameListener { - private Node _node; - private int _port; - - private DatagramSocket _udpSocket; - private ServerSocket _tcpSocket; - private DataStore _ds; - private long _nextBackgroundTaskDeadline = 0; - - private final Thread _udpReceiveThread = new Thread() { - @Override - public void run() { - try { - long[] bgtask = new long[1]; - byte[] buffer = new byte[16384]; - while(true) { - - bgtask[0] = 0; - DatagramPacket p = new DatagramPacket(buffer, buffer.length); - - try { - _udpSocket.receive(p); - if(p.getLength() > 0) - { - System.out.println("Got Data From: " + p.getAddress().toString() +":" + p.getPort()); - - _node.processWirePacket(System.currentTimeMillis(), new InetSocketAddress(p.getAddress(), p.getPort()), p.getData(), bgtask); - _nextBackgroundTaskDeadline = bgtask[0]; - } - } catch (SocketTimeoutException e) {} - } - } catch (Exception e) { - e.printStackTrace(); - } - } - }; - - - public OneService(DataStoreFileProvider prov, int port) { - this._port = port; - this._ds = new DataStore(prov); - - try { - _udpSocket = new DatagramSocket(_port); - _udpSocket.setSoTimeout(100); - _tcpSocket = new ServerSocket(); - _tcpSocket.bind(new InetSocketAddress("127.0.0.1", _port)); - } catch (SocketException e) { - e.printStackTrace(); - return; - } catch (IOException e) { - e.printStackTrace(); - return; - } - - _udpReceiveThread.start(); - - _node = new Node( - System.currentTimeMillis(), - _ds, - _ds, - this, - this, - this, - this); - } - - @Override - public void run() { - if(_node == null) - return; - - while(true) { - try { - - long dl = _nextBackgroundTaskDeadline; - long now = System.currentTimeMillis(); - - if (dl <= now) { - long[] returnDeadline = {0}; - ResultCode rc = _node.processBackgroundTasks(now, returnDeadline); - _nextBackgroundTaskDeadline = returnDeadline[0]; - - if(rc != ResultCode.RESULT_OK) { - System.out.println(rc.toString()); - } - } - - long delay = (dl > now) ? (dl - now) : 100; - Thread.sleep(delay); - - } catch (Exception ex) { - System.out.println("Exception in run loop: " + ex.getMessage()); - ex.printStackTrace(); - } - } - } - - @Override - public int onSendPacketRequested(InetSocketAddress addr, byte[] packetData) { - System.out.println("onSendPacketRequested to: " + addr.getHostString() +":"+ addr.getPort() + " "); - - if(_udpSocket == null) - return -1; - try { - DatagramPacket p = new DatagramPacket(packetData, packetData.length, addr); - _udpSocket.send(p); - System.out.println("Sent"); - } catch (Exception e) { - System.out.println("Error sending datagram: " + e.getMessage()); - return -1; - } - return 0; - } - - @Override - public void onVirtualNetworkFrame(long nwid, long srcMac, long destMac, - long etherType, long vlanId, byte[] frameData) { - // TODO Auto-generated method stub - - } - - @Override - public int onNetworkConfigurationUpdated(long nwid, - VirtualNetworkConfigOperation op, VirtualNetworkConfig config) { - // TODO Auto-generated method stub - return 0; - } - - @Override - public void onEvent(Event event) { - // TODO Auto-generated method stub - - } - - @Override - public void onNetworkError(Event event, InetSocketAddress source) { - // TODO Auto-generated method stub - - } - - @Override - public void onOutOfDate(Version newVersion) { - // TODO Auto-generated method stub - - } - - @Override - public void onTrace(String message) { - // TODO Auto-generated method stub - - } -} From b84dba3ecb2f750f5b5fda39544e20f741f961a4 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 3 Jun 2015 21:29:07 -0700 Subject: [PATCH 02/27] more logging --- java/jni/com_zerotierone_sdk_Node.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index f04058130..c84524fa8 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -416,10 +416,15 @@ namespace { { // 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); @@ -736,12 +741,14 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( if(node == NULL) { // cannot find valid node. We should never get here. + LOGE("Couldn't find a valid node!"); return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); } unsigned int nbtd_len = env->GetArrayLength(out_nextBackgroundTaskDeadline); if(nbtd_len < 1) { + LOGE("nbtd_len < 1"); return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); } @@ -751,6 +758,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( jclass inetAddressClass = cache.findClass("java/net/InetAddress"); if(inetAddressClass == NULL) { + LOGE("Can't find InetAddress class"); // can't find java.net.InetAddress return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); } @@ -849,6 +857,10 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( packetData, packetLength, &nextBackgroundTaskDeadline); + if(rc != ZT1_RESULT_OK) + { + LOGE("ZT1_Node_processWirePacket returned: %d", rc); + } jlong *outDeadline = env->GetLongArrayElements(out_nextBackgroundTaskDeadline, NULL); outDeadline[0] = (jlong)nextBackgroundTaskDeadline; From 7cc64c5cb69f2962d32ca41f6dc12499964d22a7 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 3 Jun 2015 21:29:19 -0700 Subject: [PATCH 03/27] Might help to set the enabled field on a VirtualNetworkConfig object :) --- java/jni/ZT1_jniutils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/java/jni/ZT1_jniutils.cpp b/java/jni/ZT1_jniutils.cpp index e6404e87e..36f68f9e2 100644 --- a/java/jni/ZT1_jniutils.cpp +++ b/java/jni/ZT1_jniutils.cpp @@ -822,6 +822,7 @@ jobject newNetworkConfig(JNIEnv *env, const ZT1_VirtualNetworkConfig &vnetConfig env->SetBooleanField(vnetConfigObj, dhcpField, vnetConfig.dhcp); env->SetBooleanField(vnetConfigObj, bridgeField, vnetConfig.bridge); env->SetBooleanField(vnetConfigObj, broadcastEnabledField, vnetConfig.broadcastEnabled); + env->SetBooleanField(vnetConfigObj, enabledField, vnetConfig.enabled); env->SetIntField(vnetConfigObj, portErrorField, vnetConfig.portError); jclass multicastGroupClass = cache.findClass("com/zerotier/sdk/MulticastGroup"); From ced040c5033bd61a963e65e8e8525459c4b8b59d Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 9 Jun 2015 19:38:05 -0700 Subject: [PATCH 04/27] Logging and adding .equals() methods to MulticastGroup and VirtualNetworkCofnig --- java/jni/ZT1_jniutils.cpp | 10 ++--- java/jni/com_zerotierone_sdk_Node.cpp | 12 +++--- java/src/com/zerotier/sdk/MulticastGroup.java | 4 ++ .../zerotier/sdk/VirtualNetworkConfig.java | 38 +++++++++++++++++++ 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/java/jni/ZT1_jniutils.cpp b/java/jni/ZT1_jniutils.cpp index 36f68f9e2..1e8a48bff 100644 --- a/java/jni/ZT1_jniutils.cpp +++ b/java/jni/ZT1_jniutils.cpp @@ -343,18 +343,18 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr) { case AF_INET6: { - LOGD("IPV6 Address"); + LOGV("IPV6 Address"); sockaddr_in6 *ipv6 = (sockaddr_in6*)&addr; port = ntohs(ipv6->sin6_port); - LOGD("Port %d", port); + LOGV("Port %d", port); } break; case AF_INET: { - LOGD("IPV4 Address"); + LOGV("IPV4 Address"); sockaddr_in *ipv4 = (sockaddr_in*)&addr; port = ntohs(ipv4->sin_port); - LOGD("Port: %d", port); + LOGV("Port: %d", port); } break; default: @@ -818,7 +818,7 @@ jobject newNetworkConfig(JNIEnv *env, const ZT1_VirtualNetworkConfig &vnetConfig } env->SetObjectField(vnetConfigObj, typeField, typeObject); - env->SetIntField(vnetConfigObj, mtuField, vnetConfig.mtu); + 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); diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index c84524fa8..2a90bb85b 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -92,7 +92,7 @@ namespace { enum ZT1_VirtualNetworkConfigOperation operation, const ZT1_VirtualNetworkConfig *config) { - LOGD("VritualNetworkConfigFunctionCallback"); + LOGV("VritualNetworkConfigFunctionCallback"); JniRef *ref = (JniRef*)userData; JNIEnv *env = NULL; ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6); @@ -142,7 +142,9 @@ namespace { const void *frameData, unsigned int frameLength) { - LOGD("VirtualNetworkFrameFunctionCallback"); + 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; @@ -188,7 +190,7 @@ namespace { void EventCallback(ZT1_Node *node,void *userData,enum ZT1_Event event, const void *data) { - LOGD("EventCallback"); + LOGV("EventCallback"); JniRef *ref = (JniRef*)userData; assert(ref->node == node); JNIEnv *env = NULL; @@ -436,7 +438,7 @@ namespace { const void *buffer, unsigned int bufferSize) { - LOGD("WirePacketSendFunction(%p, %p, %d)", address, buffer, bufferSize); + LOGV("WirePacketSendFunction(%p, %p, %d)", address, buffer, bufferSize); JniRef *ref = (JniRef*)userData; assert(ref->node == node); @@ -464,7 +466,7 @@ namespace { env->SetByteArrayRegion(bufferObj, 0, bufferSize, (jbyte*)buffer); int retval = env->CallIntMethod(ref->packetSender, packetSenderCallbackMethod, addressObj, bufferObj); - LOGD("JNI Packet Sender returned: %d", retval); + LOGV("JNI Packet Sender returned: %d", retval); return retval; } diff --git a/java/src/com/zerotier/sdk/MulticastGroup.java b/java/src/com/zerotier/sdk/MulticastGroup.java index 5c4df87a4..68114424f 100644 --- a/java/src/com/zerotier/sdk/MulticastGroup.java +++ b/java/src/com/zerotier/sdk/MulticastGroup.java @@ -33,6 +33,10 @@ public final class MulticastGroup { private long mac; private long adi; + public boolean equals(MulticastGroup other) { + return mac == other.mac && adi == other.adi; + } + /** * MAC address (least significant 48 bits) */ diff --git a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java index 2be03acb3..35453ddc3 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java @@ -54,6 +54,44 @@ public final class VirtualNetworkConfig { } + public boolean equals(VirtualNetworkConfig cfg) { + boolean mcgEqual = true; + if(multicastSubscriptions.length == cfg.multicastSubscriptions.length) { + for(int i = 0; i < multicastSubscriptions.length; ++i) { + if(!multicastSubscriptions[i].equals(cfg.multicastSubscriptions[i])) + { + return false; + } + } + } else { + mcgEqual = false; + } + + boolean aaEqual = true; + if(assignedAddresses.length == cfg.assignedAddresses.length) { + for(int i = 0; i < assignedAddresses.length; ++i) { + if(!assignedAddresses[i].equals(cfg.assignedAddresses[i])) { + return false; + } + } + } else { + aaEqual = false; + } + + return nwid == cfg.nwid && + mac == cfg.mac && + name.equals(cfg.name) && + status.equals(cfg.status) && + type.equals(cfg.type) && + mtu == cfg.mtu && + dhcp == cfg.dhcp && + bridge == cfg.bridge && + broadcastEnabled == cfg.broadcastEnabled && + portError == cfg.portError && + enabled == cfg.enabled && + mcgEqual && aaEqual; + } + /** * 64-bit ZeroTier network ID */ From 3013d90f579ac970e41000d6732e798589c3ac90 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 9 Jun 2015 22:38:31 -0700 Subject: [PATCH 05/27] ignore windows binary output --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 498119e3c..95f6ed5b6 100755 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,4 @@ java/doc/ java/build_win64/ java/build_win32/ /java/mac32_64/ +windows/ZeroTierOne/Debug/ From 4dc0ff8f13a2a5d852cda9302632d58b25d045ac Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 9 Jun 2015 23:12:44 -0700 Subject: [PATCH 06/27] Replace calls to GetArrayElements with GetPrimitiveArrayCritical. This puts code accessing the data in a critical section so that the GC cannot run while JNI has access to the array. This helps with stability somewhat, but I'm still getting some crashes in the GC --- java/jni/com_zerotierone_sdk_Node.cpp | 54 ++++++++++++++------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index 2a90bb85b..62fbba895 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -174,9 +174,9 @@ namespace { return; } - jbyte *data = env->GetByteArrayElements(dataArray, NULL); + void *data = env->GetPrimitiveArrayCritical(dataArray, NULL); memcpy(data, frameData, frameLength); - env->ReleaseByteArrayElements(dataArray, data, 0); + env->ReleasePrimitiveArrayCritical(dataArray, data, 0); if(env->ExceptionCheck()) { @@ -356,13 +356,13 @@ namespace { if(retval > 0) { - jbyte *data = env->GetByteArrayElements(bufferObj, NULL); + void *data = env->GetPrimitiveArrayCritical(bufferObj, NULL); memcpy(buffer, data, retval); - env->ReleaseByteArrayElements(bufferObj, data, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(bufferObj, data, 0); - jlong *objSize = env->GetLongArrayElements(objectSizeObj, NULL); + jlong *objSize = (jlong*)env->GetPrimitiveArrayCritical(objectSizeObj, NULL); *out_objectSize = (unsigned long)objSize[0]; - env->ReleaseLongArrayElements(objectSizeObj, objSize, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(objectSizeObj, objSize, 0); } LOGI("Out Object Size: %lu", *out_objectSize); @@ -700,7 +700,10 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame( unsigned int vlanId = (unsigned int)in_vlanId; unsigned int frameLength = env->GetArrayLength(in_frameData); - jbyte *frameData =env->GetByteArrayElements(in_frameData, NULL); + 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; @@ -712,15 +715,15 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame( destMac, etherType, vlanId, - (const void*)frameData, + (const void*)localData, frameLength, &nextBackgroundTaskDeadline); - jlong *outDeadline = env->GetLongArrayElements(out_nextBackgroundTaskDeadline, NULL); - outDeadline[0] = (jlong)nextBackgroundTaskDeadline; - env->ReleaseLongArrayElements(out_nextBackgroundTaskDeadline, outDeadline, 0); + - env->ReleaseByteArrayElements(in_frameData, frameData, 0); + jlong *outDeadline = (jlong*)env->GetPrimitiveArrayCritical(out_nextBackgroundTaskDeadline, NULL); + outDeadline[0] = (jlong)nextBackgroundTaskDeadline; + env->ReleasePrimitiveArrayCritical(out_nextBackgroundTaskDeadline, outDeadline, 0); return createResultObject(env, rc); } @@ -816,8 +819,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( unsigned int addrSize = env->GetArrayLength(addressArray); // get the address bytes - jbyte *addr = env->GetByteArrayElements(addressArray, NULL); - + jbyte *addr = (jbyte*)env->GetPrimitiveArrayCritical(addressArray, NULL); sockaddr_storage remoteAddress = {}; @@ -842,13 +844,16 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( else { // unknown address type - env->ReleaseByteArrayElements(addressArray, addr, 0); + env->ReleasePrimitiveArrayCritical(addressArray, addr, 0); return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); } - + env->ReleasePrimitiveArrayCritical(addressArray, addr, 0); unsigned int packetLength = env->GetArrayLength(in_packetData); - jbyte *packetData = env->GetByteArrayElements(in_packetData, NULL); + 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; @@ -856,7 +861,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( node, now, &remoteAddress, - packetData, + localData, packetLength, &nextBackgroundTaskDeadline); if(rc != ZT1_RESULT_OK) @@ -864,12 +869,11 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( LOGE("ZT1_Node_processWirePacket returned: %d", rc); } - jlong *outDeadline = env->GetLongArrayElements(out_nextBackgroundTaskDeadline, NULL); - outDeadline[0] = (jlong)nextBackgroundTaskDeadline; - env->ReleaseLongArrayElements(out_nextBackgroundTaskDeadline, outDeadline, 0); + free(localData); - env->ReleaseByteArrayElements(addressArray, addr, 0); - env->ReleaseByteArrayElements(in_packetData, packetData, 0); + jlong *outDeadline = (jlong*)env->GetPrimitiveArrayCritical(out_nextBackgroundTaskDeadline, NULL); + outDeadline[0] = (jlong)nextBackgroundTaskDeadline; + env->ReleasePrimitiveArrayCritical(out_nextBackgroundTaskDeadline, outDeadline, 0); return createResultObject(env, rc); } @@ -904,9 +908,9 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks( ZT1_ResultCode rc = ZT1_Node_processBackgroundTasks(node, now, &nextBackgroundTaskDeadline); - jlong *outDeadline = env->GetLongArrayElements(out_nextBackgroundTaskDeadline, NULL); + jlong *outDeadline = (jlong*)env->GetPrimitiveArrayCritical(out_nextBackgroundTaskDeadline, NULL); outDeadline[0] = (jlong)nextBackgroundTaskDeadline; - env->ReleaseLongArrayElements(out_nextBackgroundTaskDeadline, outDeadline, 0); + env->ReleasePrimitiveArrayCritical(out_nextBackgroundTaskDeadline, outDeadline, 0); return createResultObject(env, rc); } From 6889fcfc28ad7df236e9883c17d73c9badb8edd5 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 9 Jun 2015 23:24:47 -0700 Subject: [PATCH 07/27] Looks like it was the JNI cash causing the crash. Forcing it to look up classes and methods instead of caching them stopped the crashes in the GC. Will investigate more later. --- java/jni/ZT1_jnicache.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/java/jni/ZT1_jnicache.cpp b/java/jni/ZT1_jnicache.cpp index 8d6305cb2..4deec61f9 100644 --- a/java/jni/ZT1_jnicache.cpp +++ b/java/jni/ZT1_jnicache.cpp @@ -111,7 +111,7 @@ jclass JniCache::findClass(const std::string &name) jclass cls = (jclass)env->NewGlobalRef(localCls); - m_classes.insert(std::make_pair(name, cls)); + //m_classes.insert(std::make_pair(name, cls)); return cls; } @@ -143,7 +143,7 @@ jmethodID JniCache::findMethod(jclass cls, const std::string &methodName, const return NULL; } - m_methods.insert(std::make_pair(id, mid)); + //m_methods.insert(std::make_pair(id, mid)); return mid; } @@ -173,7 +173,7 @@ jmethodID JniCache::findStaticMethod(jclass cls, const std::string &methodName, return NULL; } - m_staticMethods.insert(std::make_pair(id, mid)); + //m_staticMethods.insert(std::make_pair(id, mid)); return mid; } @@ -203,7 +203,7 @@ jfieldID JniCache::findField(jclass cls, const std::string &fieldName, const std return NULL; } - m_fields.insert(std::make_pair(id, fid)); + //m_fields.insert(std::make_pair(id, fid)); return fid; } @@ -233,7 +233,7 @@ jfieldID JniCache::findStaticField(jclass cls, const std::string &fieldName, con return NULL; } - m_staticFields.insert(std::make_pair(id, fid)); + //m_staticFields.insert(std::make_pair(id, fid)); return fid; } From 7e84f5a7dbb50913a78311df4f748455be0e1097 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 9 Jun 2015 23:24:54 -0700 Subject: [PATCH 08/27] killing whitespace --- java/jni/com_zerotierone_sdk_Node.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index 62fbba895..9516db413 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -719,8 +719,6 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame( frameLength, &nextBackgroundTaskDeadline); - - jlong *outDeadline = (jlong*)env->GetPrimitiveArrayCritical(out_nextBackgroundTaskDeadline, NULL); outDeadline[0] = (jlong)nextBackgroundTaskDeadline; env->ReleasePrimitiveArrayCritical(out_nextBackgroundTaskDeadline, outDeadline, 0); From 472206dfb23e8c2d285d5cdf19ba1444d07e4d52 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 10 Jun 2015 20:16:13 -0700 Subject: [PATCH 09/27] Rename JniCache to JniLookup Removed caching capabilities as the cached methods, fields, and objects appears to be broken on Android --- java/CMakeLists.txt | 2 +- java/jni/Android.mk | 2 +- java/jni/ZT1_jnicache.cpp | 242 ------------------- java/jni/ZT1_jnilookup.cpp | 158 ++++++++++++ java/jni/{ZT1_jnicache.h => ZT1_jnilookup.h} | 23 +- java/jni/ZT1_jniutils.cpp | 132 +++++----- java/jni/com_zerotierone_sdk_Node.cpp | 68 +++--- 7 files changed, 266 insertions(+), 361 deletions(-) delete mode 100644 java/jni/ZT1_jnicache.cpp create mode 100644 java/jni/ZT1_jnilookup.cpp rename java/jni/{ZT1_jnicache.h => ZT1_jnilookup.h} (78%) diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt index db3eec1ca..382fd3cdb 100644 --- a/java/CMakeLists.txt +++ b/java/CMakeLists.txt @@ -54,7 +54,7 @@ set(src_files ../osdep/OSUtils.cpp jni/com_zerotierone_sdk_Node.cpp jni/ZT1_jniutils.cpp - jni/ZT1_jnicache.cpp + jni/ZT1_jnilookup.cpp ) set(include_dirs diff --git a/java/jni/Android.mk b/java/jni/Android.mk index bbf143485..9986c2c3e 100644 --- a/java/jni/Android.mk +++ b/java/jni/Android.mk @@ -39,6 +39,6 @@ LOCAL_SRC_FILES := \ LOCAL_SRC_FILES += \ com_zerotierone_sdk_Node.cpp \ ZT1_jniutils.cpp \ - ZT1_jnicache.cpp + ZT1_jnilookup.cpp include $(BUILD_SHARED_LIBRARY) \ No newline at end of file diff --git a/java/jni/ZT1_jnicache.cpp b/java/jni/ZT1_jnicache.cpp deleted file mode 100644 index 4deec61f9..000000000 --- a/java/jni/ZT1_jnicache.cpp +++ /dev/null @@ -1,242 +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 "ZT1_jnicache.h" -#include "ZT1_jniutils.h" - -JniCache::JniCache() - : m_jvm(NULL) - , m_classes() - , m_fields() - , m_staticFields() - , m_methods() - , m_staticMethods() -{ - LOGV("JNI Cache Created"); -} - -JniCache::JniCache(JavaVM *jvm) - : m_jvm(jvm) - , m_classes() - , m_fields() - , m_staticFields() - , m_methods() - , m_staticMethods() -{ - LOGV("JNI Cache Created"); -} - -JniCache::~JniCache() -{ - LOGV("JNI Cache Destroyed"); - clearCache(); -} - -void JniCache::clearCache() -{ - if(m_jvm) - { - JNIEnv *env = NULL; - if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) - return; - - for(ClassMap::iterator iter = m_classes.begin(), end = m_classes.end(); - iter != end; ++iter) - { - env->DeleteGlobalRef(iter->second); - } - } - - m_classes.clear(); - m_fields.clear(); - m_staticFields.clear(); - m_methods.clear(); - m_staticMethods.clear(); -} - -void JniCache::setJavaVM(JavaVM *jvm) -{ - LOGV("Assigned JVM to object"); - m_jvm = jvm; -} - - -jclass JniCache::findClass(const std::string &name) -{ - if(!m_jvm) - return NULL; - - ClassMap::iterator found = m_classes.find(name); - - if(found == m_classes.end()) - { - // get the class from the JVM - JNIEnv *env = NULL; - if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) - { - LOGE("Error retreiving JNI Environment"); - return NULL; - } - - jclass localCls = env->FindClass(name.c_str()); - if(env->ExceptionCheck()) - { - LOGE("Error finding class: %s", name.c_str()); - return NULL; - } - - jclass cls = (jclass)env->NewGlobalRef(localCls); - - //m_classes.insert(std::make_pair(name, cls)); - - return cls; - } - - LOGV("Returning cached %s", name.c_str()); - return found->second; -} - - -jmethodID JniCache::findMethod(jclass cls, const std::string &methodName, const std::string &methodSig) -{ - if(!m_jvm) - return NULL; - - std::string id = methodName + methodSig; - - MethodMap::iterator found = m_methods.find(id); - if(found == m_methods.end()) - { - JNIEnv *env = NULL; - if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) - { - return NULL; - } - - jmethodID mid = env->GetMethodID(cls, methodName.c_str(), methodSig.c_str()); - if(env->ExceptionCheck()) - { - return NULL; - } - - //m_methods.insert(std::make_pair(id, mid)); - - return mid; - } - - return found->second; -} - -jmethodID JniCache::findStaticMethod(jclass cls, const std::string &methodName, const std::string &methodSig) -{ - if(!m_jvm) - return NULL; - - std::string id = methodName + methodSig; - - MethodMap::iterator found = m_staticMethods.find(id); - if(found == m_staticMethods.end()) - { - JNIEnv *env = NULL; - if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) - { - return NULL; - } - - jmethodID mid = env->GetStaticMethodID(cls, methodName.c_str(), methodSig.c_str()); - if(env->ExceptionCheck()) - { - return NULL; - } - - //m_staticMethods.insert(std::make_pair(id, mid)); - - return mid; - } - - return found->second; -} - -jfieldID JniCache::findField(jclass cls, const std::string &fieldName, const std::string &typeStr) -{ - if(!m_jvm) - return NULL; - - std::string id = fieldName + typeStr; - - FieldMap::iterator found = m_fields.find(id); - if(found == m_fields.end()) - { - JNIEnv *env = NULL; - if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) - { - return NULL; - } - - jfieldID fid = env->GetFieldID(cls, fieldName.c_str(), typeStr.c_str()); - if(env->ExceptionCheck()) - { - return NULL; - } - - //m_fields.insert(std::make_pair(id, fid)); - - return fid; - } - - return found->second; -} - -jfieldID JniCache::findStaticField(jclass cls, const std::string &fieldName, const std::string &typeStr) -{ - if(!m_jvm) - return NULL; - - std::string id = fieldName + typeStr; - - FieldMap::iterator found = m_staticFields.find(id); - if(found == m_staticFields.end()) - { - JNIEnv *env = NULL; - if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) - { - return NULL; - } - - jfieldID fid = env->GetStaticFieldID(cls, fieldName.c_str(), typeStr.c_str()); - if(env->ExceptionCheck()) - { - return NULL; - } - - //m_staticFields.insert(std::make_pair(id, fid)); - - return fid; - } - - return found->second; -} \ No newline at end of file diff --git a/java/jni/ZT1_jnilookup.cpp b/java/jni/ZT1_jnilookup.cpp new file mode 100644 index 000000000..d8f5cc7f1 --- /dev/null +++ b/java/jni/ZT1_jnilookup.cpp @@ -0,0 +1,158 @@ +/* + * 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 "ZT1_jnilookup.h" +#include "ZT1_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 retreiving JNI Environment"); + return NULL; + } + + jclass cls = env->FindClass(name.c_str()); + 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/ZT1_jnicache.h b/java/jni/ZT1_jnilookup.h similarity index 78% rename from java/jni/ZT1_jnicache.h rename to java/jni/ZT1_jnilookup.h index 43f43a08c..cf496f882 100644 --- a/java/jni/ZT1_jnicache.h +++ b/java/jni/ZT1_jnilookup.h @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef ZT1_JNICACHE_H_ -#define ZT1_JNICACHE_H_ +#ifndef ZT1_JNILOOKUP_H_ +#define ZT1_JNILOOKUP_H_ #include #include @@ -34,14 +34,13 @@ -class JniCache { +class JniLookup { public: - JniCache(); - JniCache(JavaVM *jvm); - ~JniCache(); + JniLookup(); + JniLookup(JavaVM *jvm); + ~JniLookup(); void setJavaVM(JavaVM *jvm); - void clearCache(); jclass findClass(const std::string &name); jmethodID findMethod(jclass cls, const std::string &methodName, const std::string &methodSig); @@ -49,17 +48,7 @@ public: jfieldID findField(jclass cls, const std::string &fieldName, const std::string &typeStr); jfieldID findStaticField(jclass cls, const std::string &fieldName, const std::string &typeStr); private: - typedef std::map MethodMap; - typedef std::map FieldMap; - typedef std::map ClassMap; - JavaVM *m_jvm; - ClassMap m_classes; - FieldMap m_fields; - FieldMap m_staticFields; - MethodMap m_methods; - MethodMap m_staticMethods; - }; #endif \ No newline at end of file diff --git a/java/jni/ZT1_jniutils.cpp b/java/jni/ZT1_jniutils.cpp index 1e8a48bff..ac04eb58b 100644 --- a/java/jni/ZT1_jniutils.cpp +++ b/java/jni/ZT1_jniutils.cpp @@ -1,9 +1,9 @@ #include "ZT1_jniutils.h" -#include "ZT1_jnicache.h" +#include "ZT1_jnilookup.h" #include #include -extern JniCache cache; +extern JniLookup lookup; #ifdef __cplusplus extern "C" { @@ -15,7 +15,7 @@ jobject createResultObject(JNIEnv *env, ZT1_ResultCode code) jobject resultObject = NULL; - resultClass = cache.findClass("com/zerotier/sdk/ResultCode"); + resultClass = lookup.findClass("com/zerotier/sdk/ResultCode"); if(resultClass == NULL) { LOGE("Couldnt find ResultCode class"); @@ -48,7 +48,7 @@ jobject createResultObject(JNIEnv *env, ZT1_ResultCode code) break; } - jfieldID enumField = cache.findStaticField(resultClass, fieldName.c_str(), "Lcom/zerotier/sdk/ResultCode;"); + jfieldID enumField = lookup.findStaticField(resultClass, fieldName.c_str(), "Lcom/zerotier/sdk/ResultCode;"); if(env->ExceptionCheck() || enumField == NULL) { LOGE("Error on FindStaticField"); @@ -68,7 +68,7 @@ jobject createVirtualNetworkStatus(JNIEnv *env, ZT1_VirtualNetworkStatus status) { jobject statusObject = NULL; - jclass statusClass = cache.findClass("com/zerotier/sdk/VirtualNetworkStatus"); + jclass statusClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkStatus"); if(statusClass == NULL) { return NULL; // exception thrown @@ -97,7 +97,7 @@ jobject createVirtualNetworkStatus(JNIEnv *env, ZT1_VirtualNetworkStatus status) break; } - jfieldID enumField = cache.findStaticField(statusClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkStatus;"); + jfieldID enumField = lookup.findStaticField(statusClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkStatus;"); statusObject = env->GetStaticObjectField(statusClass, enumField); @@ -109,7 +109,7 @@ jobject createEvent(JNIEnv *env, ZT1_Event event) jclass eventClass = NULL; jobject eventObject = NULL; - eventClass = cache.findClass("com/zerotier/sdk/Event"); + eventClass = lookup.findClass("com/zerotier/sdk/Event"); if(eventClass == NULL) { return NULL; @@ -147,7 +147,7 @@ jobject createEvent(JNIEnv *env, ZT1_Event event) break; } - jfieldID enumField = cache.findStaticField(eventClass, fieldName.c_str(), "Lcom/zerotier/sdk/Event;"); + jfieldID enumField = lookup.findStaticField(eventClass, fieldName.c_str(), "Lcom/zerotier/sdk/Event;"); eventObject = env->GetStaticObjectField(eventClass, enumField); @@ -159,7 +159,7 @@ jobject createPeerRole(JNIEnv *env, ZT1_PeerRole role) jclass peerRoleClass = NULL; jobject peerRoleObject = NULL; - peerRoleClass = cache.findClass("com/zerotier/sdk/PeerRole"); + peerRoleClass = lookup.findClass("com/zerotier/sdk/PeerRole"); if(peerRoleClass == NULL) { return NULL; @@ -179,7 +179,7 @@ jobject createPeerRole(JNIEnv *env, ZT1_PeerRole role) break; } - jfieldID enumField = cache.findStaticField(peerRoleClass, fieldName.c_str(), "Lcom/zerotier/sdk/PeerRole;"); + jfieldID enumField = lookup.findStaticField(peerRoleClass, fieldName.c_str(), "Lcom/zerotier/sdk/PeerRole;"); peerRoleObject = env->GetStaticObjectField(peerRoleClass, enumField); @@ -191,7 +191,7 @@ jobject createVirtualNetworkType(JNIEnv *env, ZT1_VirtualNetworkType type) jclass vntypeClass = NULL; jobject vntypeObject = NULL; - vntypeClass = cache.findClass("com/zerotier/sdk/VirtualNetworkType"); + vntypeClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkType"); if(env->ExceptionCheck() || vntypeClass == NULL) { return NULL; @@ -208,7 +208,7 @@ jobject createVirtualNetworkType(JNIEnv *env, ZT1_VirtualNetworkType type) break; } - jfieldID enumField = cache.findStaticField(vntypeClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkType;"); + jfieldID enumField = lookup.findStaticField(vntypeClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkType;"); vntypeObject = env->GetStaticObjectField(vntypeClass, enumField); return vntypeObject; } @@ -218,7 +218,7 @@ jobject createVirtualNetworkConfigOperation(JNIEnv *env, ZT1_VirtualNetworkConfi jclass vnetConfigOpClass = NULL; jobject vnetConfigOpObject = NULL; - vnetConfigOpClass = cache.findClass("com/zerotier/sdk/VirtualNetworkConfigOperation"); + vnetConfigOpClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfigOperation"); if(env->ExceptionCheck() || vnetConfigOpClass == NULL) { return NULL; @@ -241,7 +241,7 @@ jobject createVirtualNetworkConfigOperation(JNIEnv *env, ZT1_VirtualNetworkConfi break; } - jfieldID enumField = cache.findStaticField(vnetConfigOpClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkConfigOperation;"); + jfieldID enumField = lookup.findStaticField(vnetConfigOpClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkConfigOperation;"); vnetConfigOpObject = env->GetStaticObjectField(vnetConfigOpClass, enumField); return vnetConfigOpObject; } @@ -252,14 +252,14 @@ jobject newInetAddress(JNIEnv *env, const sockaddr_storage &addr) jclass inetAddressClass = NULL; jmethodID inetAddress_getByAddress = NULL; - inetAddressClass = cache.findClass("java/net/InetAddress"); + inetAddressClass = lookup.findClass("java/net/InetAddress"); if(env->ExceptionCheck() || inetAddressClass == NULL) { LOGE("Error finding InetAddress class"); return NULL; } - inetAddress_getByAddress = cache.findStaticMethod( + inetAddress_getByAddress = lookup.findStaticMethod( inetAddressClass, "getByAddress", "([B)Ljava/net/InetAddress;"); if(env->ExceptionCheck() || inetAddress_getByAddress == NULL) { @@ -315,7 +315,7 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr) jclass inetSocketAddressClass = NULL; jmethodID inetSocketAddress_constructor = NULL; - inetSocketAddressClass = cache.findClass("java/net/InetSocketAddress"); + inetSocketAddressClass = lookup.findClass("java/net/InetSocketAddress"); if(env->ExceptionCheck() || inetSocketAddressClass == NULL) { LOGE("Error finding InetSocketAddress Class"); @@ -330,7 +330,7 @@ jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr) return NULL; } - inetSocketAddress_constructor = cache.findMethod( + inetSocketAddress_constructor = lookup.findMethod( inetSocketAddressClass, "", "(Ljava/net/InetAddress;I)V"); if(env->ExceptionCheck() || inetSocketAddress_constructor == NULL) { @@ -380,13 +380,13 @@ jobject newMulticastGroup(JNIEnv *env, const ZT1_MulticastGroup &mc) jfieldID macField = NULL; jfieldID adiField = NULL; - multicastGroupClass = cache.findClass("com/zerotier/sdk/MulticastGroup"); + multicastGroupClass = lookup.findClass("com/zerotier/sdk/MulticastGroup"); if(env->ExceptionCheck() || multicastGroupClass == NULL) { return NULL; } - multicastGroup_constructor = cache.findMethod( + multicastGroup_constructor = lookup.findMethod( multicastGroupClass, "", "()V"); if(env->ExceptionCheck() || multicastGroup_constructor == NULL) { @@ -399,13 +399,13 @@ jobject newMulticastGroup(JNIEnv *env, const ZT1_MulticastGroup &mc) return NULL; } - macField = cache.findField(multicastGroupClass, "mac", "J"); + macField = lookup.findField(multicastGroupClass, "mac", "J"); if(env->ExceptionCheck() || macField == NULL) { return NULL; } - adiField = cache.findField(multicastGroupClass, "adi", "J"); + adiField = lookup.findField(multicastGroupClass, "adi", "J"); if(env->ExceptionCheck() || adiField == NULL) { return NULL; @@ -431,56 +431,56 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT1_PeerPhysicalPath &ppp) jmethodID ppp_constructor = NULL; - pppClass = cache.findClass("com/zerotier/sdk/PeerPhysicalPath"); + pppClass = lookup.findClass("com/zerotier/sdk/PeerPhysicalPath"); if(env->ExceptionCheck() || pppClass == NULL) { LOGE("Error finding PeerPhysicalPath class"); return NULL; } - addressField = cache.findField(pppClass, "address", "Ljava/net/InetSocketAddress;"); + addressField = lookup.findField(pppClass, "address", "Ljava/net/InetSocketAddress;"); if(env->ExceptionCheck() || addressField == NULL) { LOGE("Error finding address field"); return NULL; } - lastSendField = cache.findField(pppClass, "lastSend", "J"); + lastSendField = lookup.findField(pppClass, "lastSend", "J"); if(env->ExceptionCheck() || lastSendField == NULL) { LOGE("Error finding lastSend field"); return NULL; } - lastReceiveField = cache.findField(pppClass, "lastReceive", "J"); + lastReceiveField = lookup.findField(pppClass, "lastReceive", "J"); if(env->ExceptionCheck() || lastReceiveField == NULL) { LOGE("Error finding lastReceive field"); return NULL; } - fixedField = cache.findField(pppClass, "fixed", "Z"); + fixedField = lookup.findField(pppClass, "fixed", "Z"); if(env->ExceptionCheck() || fixedField == NULL) { LOGE("Error finding fixed field"); return NULL; } - activeField = cache.findField(pppClass, "active", "Z"); + activeField = lookup.findField(pppClass, "active", "Z"); if(env->ExceptionCheck() || activeField == NULL) { LOGE("Error finding active field"); return NULL; } - preferredField = cache.findField(pppClass, "preferred", "Z"); + preferredField = lookup.findField(pppClass, "preferred", "Z"); if(env->ExceptionCheck() || preferredField == NULL) { LOGE("Error finding preferred field"); return NULL; } - ppp_constructor = cache.findMethod(pppClass, "", "()V"); + ppp_constructor = lookup.findMethod(pppClass, "", "()V"); if(env->ExceptionCheck() || ppp_constructor == NULL) { LOGE("Error finding PeerPhysicalPath constructor"); @@ -532,77 +532,77 @@ jobject newPeer(JNIEnv *env, const ZT1_Peer &peer) jmethodID peer_constructor = NULL; - peerClass = cache.findClass("com/zerotier/sdk/Peer"); + peerClass = lookup.findClass("com/zerotier/sdk/Peer"); if(env->ExceptionCheck() || peerClass == NULL) { LOGE("Error finding Peer class"); return NULL; } - addressField = cache.findField(peerClass, "address", "J"); + addressField = lookup.findField(peerClass, "address", "J"); if(env->ExceptionCheck() || addressField == NULL) { LOGE("Error finding address field of Peer object"); return NULL; } - lastUnicastFrameField = cache.findField(peerClass, "lastUnicastFrame", "J"); + lastUnicastFrameField = lookup.findField(peerClass, "lastUnicastFrame", "J"); if(env->ExceptionCheck() || lastUnicastFrameField == NULL) { LOGE("Error finding lastUnicastFrame field of Peer object"); return NULL; } - lastMulticastFrameField = cache.findField(peerClass, "lastMulticastFrame", "J"); + lastMulticastFrameField = lookup.findField(peerClass, "lastMulticastFrame", "J"); if(env->ExceptionCheck() || lastMulticastFrameField == NULL) { LOGE("Error finding lastMulticastFrame field of Peer object"); return NULL; } - versionMajorField = cache.findField(peerClass, "versionMajor", "I"); + versionMajorField = lookup.findField(peerClass, "versionMajor", "I"); if(env->ExceptionCheck() || versionMajorField == NULL) { LOGE("Error finding versionMajor field of Peer object"); return NULL; } - versionMinorField = cache.findField(peerClass, "versionMinor", "I"); + versionMinorField = lookup.findField(peerClass, "versionMinor", "I"); if(env->ExceptionCheck() || versionMinorField == NULL) { LOGE("Error finding versionMinor field of Peer object"); return NULL; } - versionRevField = cache.findField(peerClass, "versionRev", "I"); + versionRevField = lookup.findField(peerClass, "versionRev", "I"); if(env->ExceptionCheck() || versionRevField == NULL) { LOGE("Error finding versionRev field of Peer object"); return NULL; } - latencyField = cache.findField(peerClass, "latency", "I"); + latencyField = lookup.findField(peerClass, "latency", "I"); if(env->ExceptionCheck() || latencyField == NULL) { LOGE("Error finding latency field of Peer object"); return NULL; } - roleField = cache.findField(peerClass, "role", "Lcom/zerotier/sdk/PeerRole;"); + 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 = cache.findField(peerClass, "paths", "[Lcom/zerotier/sdk/PeerPhysicalPath;"); + 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 = cache.findMethod(peerClass, "", "()V"); + peer_constructor = lookup.findMethod(peerClass, "", "()V"); if(env->ExceptionCheck() || peer_constructor == NULL) { LOGE("Error finding Peer constructor"); @@ -625,7 +625,7 @@ jobject newPeer(JNIEnv *env, const ZT1_Peer &peer) env->SetIntField(peerObject, latencyField, peer.latency); env->SetObjectField(peerObject, roleField, createPeerRole(env, peer.role)); - jclass peerPhysicalPathClass = cache.findClass("com/zerotier/sdk/PeerPhysicalPath"); + jclass peerPhysicalPathClass = lookup.findClass("com/zerotier/sdk/PeerPhysicalPath"); if(env->ExceptionCheck() || peerPhysicalPathClass == NULL) { LOGE("Error finding PeerPhysicalPath class"); @@ -675,14 +675,14 @@ jobject newNetworkConfig(JNIEnv *env, const ZT1_VirtualNetworkConfig &vnetConfig jfieldID multicastSubscriptionsField = NULL; jfieldID assignedAddressesField = NULL; - vnetConfigClass = cache.findClass("com/zerotier/sdk/VirtualNetworkConfig"); + vnetConfigClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfig"); if(vnetConfigClass == NULL) { LOGE("Couldn't find com.zerotier.sdk.VirtualNetworkConfig"); return NULL; } - vnetConfig_constructor = cache.findMethod( + vnetConfig_constructor = lookup.findMethod( vnetConfigClass, "", "()V"); if(env->ExceptionCheck() || vnetConfig_constructor == NULL) { @@ -697,98 +697,98 @@ jobject newNetworkConfig(JNIEnv *env, const ZT1_VirtualNetworkConfig &vnetConfig return NULL; } - nwidField = cache.findField(vnetConfigClass, "nwid", "J"); + nwidField = lookup.findField(vnetConfigClass, "nwid", "J"); if(env->ExceptionCheck() || nwidField == NULL) { LOGE("Error getting nwid field"); return NULL; } - macField = cache.findField(vnetConfigClass, "mac", "J"); + macField = lookup.findField(vnetConfigClass, "mac", "J"); if(env->ExceptionCheck() || macField == NULL) { LOGE("Error getting mac field"); return NULL; } - nameField = cache.findField(vnetConfigClass, "name", "Ljava/lang/String;"); + nameField = lookup.findField(vnetConfigClass, "name", "Ljava/lang/String;"); if(env->ExceptionCheck() || nameField == NULL) { LOGE("Error getting name field"); return NULL; } - statusField = cache.findField(vnetConfigClass, "status", "Lcom/zerotier/sdk/VirtualNetworkStatus;"); + statusField = lookup.findField(vnetConfigClass, "status", "Lcom/zerotier/sdk/VirtualNetworkStatus;"); if(env->ExceptionCheck() || statusField == NULL) { LOGE("Error getting status field"); return NULL; } - typeField = cache.findField(vnetConfigClass, "type", "Lcom/zerotier/sdk/VirtualNetworkType;"); + typeField = lookup.findField(vnetConfigClass, "type", "Lcom/zerotier/sdk/VirtualNetworkType;"); if(env->ExceptionCheck() || typeField == NULL) { LOGE("Error getting type field"); return NULL; } - mtuField = cache.findField(vnetConfigClass, "mtu", "I"); + mtuField = lookup.findField(vnetConfigClass, "mtu", "I"); if(env->ExceptionCheck() || mtuField == NULL) { LOGE("Error getting mtu field"); return NULL; } - dhcpField = cache.findField(vnetConfigClass, "dhcp", "Z"); + dhcpField = lookup.findField(vnetConfigClass, "dhcp", "Z"); if(env->ExceptionCheck() || dhcpField == NULL) { LOGE("Error getting dhcp field"); return NULL; } - bridgeField = cache.findField(vnetConfigClass, "bridge", "Z"); + bridgeField = lookup.findField(vnetConfigClass, "bridge", "Z"); if(env->ExceptionCheck() || bridgeField == NULL) { LOGE("Error getting bridge field"); return NULL; } - broadcastEnabledField = cache.findField(vnetConfigClass, "broadcastEnabled", "Z"); + broadcastEnabledField = lookup.findField(vnetConfigClass, "broadcastEnabled", "Z"); if(env->ExceptionCheck() || broadcastEnabledField == NULL) { LOGE("Error getting broadcastEnabled field"); return NULL; } - portErrorField = cache.findField(vnetConfigClass, "portError", "I"); + portErrorField = lookup.findField(vnetConfigClass, "portError", "I"); if(env->ExceptionCheck() || portErrorField == NULL) { LOGE("Error getting portError field"); return NULL; } - enabledField = cache.findField(vnetConfigClass, "enabled", "Z"); + enabledField = lookup.findField(vnetConfigClass, "enabled", "Z"); if(env->ExceptionCheck() || enabledField == NULL) { LOGE("Error getting enabled field"); return NULL; } - netconfRevisionField = cache.findField(vnetConfigClass, "netconfRevision", "J"); + netconfRevisionField = lookup.findField(vnetConfigClass, "netconfRevision", "J"); if(env->ExceptionCheck() || netconfRevisionField == NULL) { LOGE("Error getting netconfRevision field"); return NULL; } - multicastSubscriptionsField = cache.findField(vnetConfigClass, "multicastSubscriptions", "[Lcom/zerotier/sdk/MulticastGroup;"); + multicastSubscriptionsField = lookup.findField(vnetConfigClass, "multicastSubscriptions", "[Lcom/zerotier/sdk/MulticastGroup;"); if(env->ExceptionCheck() || multicastSubscriptionsField == NULL) { LOGE("Error getting multicastSubscriptions field"); return NULL; } - assignedAddressesField = cache.findField(vnetConfigClass, "assignedAddresses", "[Ljava/net/InetSocketAddress;"); + assignedAddressesField = lookup.findField(vnetConfigClass, "assignedAddresses", "[Ljava/net/InetSocketAddress;"); if(env->ExceptionCheck() || assignedAddressesField == NULL) { LOGE("Error getting assignedAddresses field"); @@ -825,7 +825,7 @@ jobject newNetworkConfig(JNIEnv *env, const ZT1_VirtualNetworkConfig &vnetConfig env->SetBooleanField(vnetConfigObj, enabledField, vnetConfig.enabled); env->SetIntField(vnetConfigObj, portErrorField, vnetConfig.portError); - jclass multicastGroupClass = cache.findClass("com/zerotier/sdk/MulticastGroup"); + jclass multicastGroupClass = lookup.findClass("com/zerotier/sdk/MulticastGroup"); if(env->ExceptionCheck() || multicastGroupClass == NULL) { LOGE("Error finding MulticastGroup class"); @@ -850,7 +850,7 @@ jobject newNetworkConfig(JNIEnv *env, const ZT1_VirtualNetworkConfig &vnetConfig } env->SetObjectField(vnetConfigObj, multicastSubscriptionsField, mcastSubsArrayObj); - jclass inetSocketAddressClass = cache.findClass("java/net/InetSocketAddress"); + jclass inetSocketAddressClass = lookup.findClass("java/net/InetSocketAddress"); if(env->ExceptionCheck() || inetSocketAddressClass == NULL) { LOGE("Error finding InetSocketAddress class"); @@ -887,13 +887,13 @@ jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags jclass versionClass = NULL; jmethodID versionConstructor = NULL; - versionClass = cache.findClass("com/zerotier/sdk/Version"); + versionClass = lookup.findClass("com/zerotier/sdk/Version"); if(env->ExceptionCheck() || versionClass == NULL) { return NULL; } - versionConstructor = cache.findMethod( + versionConstructor = lookup.findMethod( versionClass, "", "()V"); if(env->ExceptionCheck() || versionConstructor == NULL) { @@ -912,25 +912,25 @@ jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags jfieldID revisionField = NULL; jfieldID featureFlagsField = NULL; - majorField = cache.findField(versionClass, "major", "I"); + majorField = lookup.findField(versionClass, "major", "I"); if(env->ExceptionCheck() || majorField == NULL) { return NULL; } - minorField = cache.findField(versionClass, "minor", "I"); + minorField = lookup.findField(versionClass, "minor", "I"); if(env->ExceptionCheck() || minorField == NULL) { return NULL; } - revisionField = cache.findField(versionClass, "revision", "I"); + revisionField = lookup.findField(versionClass, "revision", "I"); if(env->ExceptionCheck() || revisionField == NULL) { return NULL; } - featureFlagsField = cache.findField(versionClass, "featureFlags", "J"); + featureFlagsField = lookup.findField(versionClass, "featureFlags", "J"); if(env->ExceptionCheck() || featureFlagsField == NULL) { return NULL; diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index 9516db413..aa42c5488 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -27,7 +27,7 @@ #include "com_zerotierone_sdk_Node.h" #include "ZT1_jniutils.h" -#include "ZT1_jnicache.h" +#include "ZT1_jnilookup.h" #include @@ -36,8 +36,8 @@ #include #include -// global static JNI Cache Object -JniCache cache; +// global static JNI Lookup Object +JniLookup lookup; #ifdef __cplusplus extern "C" { @@ -104,7 +104,7 @@ namespace { return -1; } - jmethodID configListenerCallbackMethod = cache.findMethod(configListenerClass, + jmethodID configListenerCallbackMethod = lookup.findMethod(configListenerClass, "onNetworkConfigurationUpdated", "(JLcom/zerotier/sdk/VirtualNetworkConfigOperation;Lcom/zerotier/sdk/VirtualNetworkConfig;)I"); if(configListenerCallbackMethod == NULL) @@ -158,7 +158,7 @@ namespace { return; } - jmethodID frameListenerCallbackMethod = cache.findMethod( + jmethodID frameListenerCallbackMethod = lookup.findMethod( frameListenerClass, "onVirtualNetworkFrame", "(JJJJJ[B)V"); if(env->ExceptionCheck() || frameListenerCallbackMethod == NULL) @@ -204,7 +204,7 @@ namespace { return; } - jmethodID onEventMethod = cache.findMethod(eventListenerClass, + jmethodID onEventMethod = lookup.findMethod(eventListenerClass, "onEvent", "(Lcom/zerotier/sdk/Event;)V"); if(onEventMethod == NULL) { @@ -213,7 +213,7 @@ namespace { } - jmethodID onOutOfDateMethod = cache.findMethod(eventListenerClass, + jmethodID onOutOfDateMethod = lookup.findMethod(eventListenerClass, "onOutOfDate", "(Lcom/zerotier/sdk/Version;)V"); if(onOutOfDateMethod == NULL) { @@ -222,7 +222,7 @@ namespace { } - jmethodID onNetworkErrorMethod = cache.findMethod(eventListenerClass, + jmethodID onNetworkErrorMethod = lookup.findMethod(eventListenerClass, "onNetworkError", "(Lcom/zerotier/sdk/Event;Ljava/net/InetSocketAddress;)V"); if(onNetworkErrorMethod == NULL) { @@ -231,7 +231,7 @@ namespace { } - jmethodID onTraceMethod = cache.findMethod(eventListenerClass, + jmethodID onTraceMethod = lookup.findMethod(eventListenerClass, "onTrace", "(Ljava/lang/String;)V"); if(onTraceMethod == NULL) { @@ -316,7 +316,7 @@ namespace { return -2; } - jmethodID dataStoreGetCallbackMethod = cache.findMethod( + jmethodID dataStoreGetCallbackMethod = lookup.findMethod( dataStoreGetClass, "onDataStoreGet", "(Ljava/lang/String;[BJ[J)J"); @@ -388,7 +388,7 @@ namespace { return -1; } - jmethodID dataStorePutCallbackMethod = cache.findMethod( + jmethodID dataStorePutCallbackMethod = lookup.findMethod( dataStorePutClass, "onDataStorePut", "(Ljava/lang/String;[BZ)I"); @@ -398,7 +398,7 @@ namespace { return -2; } - jmethodID deleteMethod = cache.findMethod(dataStorePutClass, + jmethodID deleteMethod = lookup.findMethod(dataStorePutClass, "onDelete", "(Ljava/lang/String;)I"); if(deleteMethod == NULL) { @@ -453,7 +453,7 @@ namespace { return -1; } - jmethodID packetSenderCallbackMethod = cache.findMethod(packetSenderClass, + jmethodID packetSenderCallbackMethod = lookup.findMethod(packetSenderClass, "onSendPacketRequested", "(Ljava/net/InetSocketAddress;[B)I"); if(packetSenderCallbackMethod == NULL) { @@ -487,13 +487,13 @@ namespace { JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { - cache.setJavaVM(vm); + lookup.setJavaVM(vm); return JNI_VERSION_1_6; } JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { - cache.clearCache(); + } @@ -514,7 +514,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( env->GetJavaVM(&ref->jvm); jclass cls = env->GetObjectClass(obj); - jfieldID fid = cache.findField( + jfieldID fid = lookup.findField( cls, "getListener", "Lcom/zerotier/sdk/DataStoreGetListener;"); if(fid == NULL) @@ -529,7 +529,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( } ref->dataStoreGetListener = env->NewGlobalRef(tmp); - fid = cache.findField( + fid = lookup.findField( cls, "putListener", "Lcom/zerotier/sdk/DataStorePutListener;"); if(fid == NULL) @@ -544,7 +544,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( } ref->dataStorePutListener = env->NewGlobalRef(tmp); - fid = cache.findField( + fid = lookup.findField( cls, "sender", "Lcom/zerotier/sdk/PacketSender;"); if(fid == NULL) { @@ -558,7 +558,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( } ref->packetSender = env->NewGlobalRef(tmp); - fid = cache.findField( + fid = lookup.findField( cls, "frameListener", "Lcom/zerotier/sdk/VirtualNetworkFrameListener;"); if(fid == NULL) { @@ -572,7 +572,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( } ref->frameListener = env->NewGlobalRef(tmp); - fid = cache.findField( + fid = lookup.findField( cls, "configListener", "Lcom/zerotier/sdk/VirtualNetworkConfigListener;"); if(fid == NULL) { @@ -586,7 +586,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( } ref->configListener = env->NewGlobalRef(tmp); - fid = cache.findField( + fid = lookup.findField( cls, "eventListener", "Lcom/zerotier/sdk/EventListener;"); if(fid == NULL) { @@ -758,7 +758,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( uint64_t now = (uint64_t)in_now; // get the java.net.InetSocketAddress class and getAddress() method - jclass inetAddressClass = cache.findClass("java/net/InetAddress"); + jclass inetAddressClass = lookup.findClass("java/net/InetAddress"); if(inetAddressClass == NULL) { LOGE("Can't find InetAddress class"); @@ -766,7 +766,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); } - jmethodID getAddressMethod = cache.findMethod( + jmethodID getAddressMethod = lookup.findMethod( inetAddressClass, "getAddress", "()[B"); if(getAddressMethod == NULL) { @@ -774,13 +774,13 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); } - jclass InetSocketAddressClass = cache.findClass("java/net/InetSocketAddress"); + jclass InetSocketAddressClass = lookup.findClass("java/net/InetSocketAddress"); if(InetSocketAddressClass == NULL) { return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); } - jmethodID inetSockGetAddressMethod = cache.findMethod( + jmethodID inetSockGetAddressMethod = lookup.findMethod( InetSocketAddressClass, "getAddress", "()Ljava/net/InetAddress;"); jobject addrObject = env->CallObjectMethod(in_remoteAddress, inetSockGetAddressMethod); @@ -790,7 +790,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); } - jmethodID inetSock_getPort = cache.findMethod( + jmethodID inetSock_getPort = lookup.findMethod( InetSocketAddressClass, "getPort", "()I"); if(env->ExceptionCheck() || inetSock_getPort == NULL) @@ -1059,13 +1059,13 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status jmethodID nodeStatusConstructor = NULL; // create a com.zerotier.sdk.NodeStatus object - nodeStatusClass = cache.findClass("com/zerotier/sdk/NodeStatus"); + nodeStatusClass = lookup.findClass("com/zerotier/sdk/NodeStatus"); if(nodeStatusClass == NULL) { return NULL; } - nodeStatusConstructor = cache.findMethod( + nodeStatusConstructor = lookup.findMethod( nodeStatusClass, "", "()V"); if(nodeStatusConstructor == NULL) { @@ -1086,25 +1086,25 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status jfieldID secretIdentityField = NULL; jfieldID onlineField = NULL; - addressField = cache.findField(nodeStatusClass, "address", "J"); + addressField = lookup.findField(nodeStatusClass, "address", "J"); if(addressField == NULL) { return NULL; } - publicIdentityField = cache.findField(nodeStatusClass, "publicIdentity", "Ljava/lang/String;"); + publicIdentityField = lookup.findField(nodeStatusClass, "publicIdentity", "Ljava/lang/String;"); if(publicIdentityField == NULL) { return NULL; } - secretIdentityField = cache.findField(nodeStatusClass, "secretIdentity", "Ljava/lang/String;"); + secretIdentityField = lookup.findField(nodeStatusClass, "secretIdentity", "Ljava/lang/String;"); if(secretIdentityField == NULL) { return NULL; } - onlineField = cache.findField(nodeStatusClass, "online", "Z"); + onlineField = lookup.findField(nodeStatusClass, "online", "Z"); if(onlineField == NULL) { return NULL; @@ -1207,7 +1207,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers( return NULL; } - jclass peerClass = cache.findClass("com/zerotier/sdk/Peer"); + jclass peerClass = lookup.findClass("com/zerotier/sdk/Peer"); if(env->ExceptionCheck() || peerClass == NULL) { LOGE("Error finding Peer class"); @@ -1265,7 +1265,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_networks( return NULL; } - jclass vnetConfigClass = cache.findClass("com/zerotier/sdk/VirtualNetworkConfig"); + jclass vnetConfigClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfig"); if(env->ExceptionCheck() || vnetConfigClass == NULL) { LOGE("Error finding VirtualNetworkConfig class"); From abbcb0a12cbab782c1d9879391efb65e9cd92acf Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sat, 13 Jun 2015 14:38:04 -0700 Subject: [PATCH 10/27] Modified ant build script so that it can be integrated with Android Studio's build system --- java/build.xml | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/java/build.xml b/java/build.xml index e24a0e13c..93d754a64 100644 --- a/java/build.xml +++ b/java/build.xml @@ -1,4 +1,4 @@ - + @@ -9,7 +9,7 @@ - + @@ -24,6 +24,10 @@ + + + + - + @@ -91,7 +95,7 @@ overwrite="true"/> - + @@ -101,23 +105,4 @@ - - \ No newline at end of file From adf89901c4d2de0d2338889a7fa1e5df1ab6b9be Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 19 Jun 2015 19:00:06 -0700 Subject: [PATCH 11/27] dont consider multicast groups in VirtualNetworkConfig.equals() --- .../src/com/zerotier/sdk/VirtualNetworkConfig.java | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java index 35453ddc3..980f48f70 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java @@ -55,18 +55,6 @@ public final class VirtualNetworkConfig { } public boolean equals(VirtualNetworkConfig cfg) { - boolean mcgEqual = true; - if(multicastSubscriptions.length == cfg.multicastSubscriptions.length) { - for(int i = 0; i < multicastSubscriptions.length; ++i) { - if(!multicastSubscriptions[i].equals(cfg.multicastSubscriptions[i])) - { - return false; - } - } - } else { - mcgEqual = false; - } - boolean aaEqual = true; if(assignedAddresses.length == cfg.assignedAddresses.length) { for(int i = 0; i < assignedAddresses.length; ++i) { @@ -89,7 +77,7 @@ public final class VirtualNetworkConfig { broadcastEnabled == cfg.broadcastEnabled && portError == cfg.portError && enabled == cfg.enabled && - mcgEqual && aaEqual; + aaEqual; } /** From 759d71037ef429c0277d8bebe16cc4ac1f426c45 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 23 Jun 2015 23:03:02 -0700 Subject: [PATCH 12/27] added Comparable interface to VirtualNetworkConfig so we can sort arrays containing it. --- java/src/com/zerotier/sdk/VirtualNetworkConfig.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java index 980f48f70..278fafd26 100644 --- a/java/src/com/zerotier/sdk/VirtualNetworkConfig.java +++ b/java/src/com/zerotier/sdk/VirtualNetworkConfig.java @@ -27,11 +27,13 @@ package com.zerotier.sdk; +import java.lang.Comparable; +import java.lang.Override; import java.lang.String; import java.util.ArrayList; import java.net.InetSocketAddress; -public final class VirtualNetworkConfig { +public final class VirtualNetworkConfig implements Comparable { public static final int MAX_MULTICAST_SUBSCRIPTIONS = 4096; public static final int ZT1_MAX_ZT_ASSIGNED_ADDRESSES = 16; @@ -80,6 +82,14 @@ public final class VirtualNetworkConfig { aaEqual; } + public int compareTo(VirtualNetworkConfig cfg) { + if(cfg.nwid == this.nwid) { + return 0; + } else { + return this.nwid > cfg.nwid ? 1 : -1; + } + } + /** * 64-bit ZeroTier network ID */ From bfb152f53f528934583ee76437453a005610a7ea Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 24 Jun 2015 20:31:22 -0700 Subject: [PATCH 13/27] configure the NDK to build all supported ABIs and package them up in the jar --- java/build.xml | 15 ++++++++++++--- java/jni/Application.mk | 5 +++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/java/build.xml b/java/build.xml index 93d754a64..4bc1cdc08 100644 --- a/java/build.xml +++ b/java/build.xml @@ -43,18 +43,27 @@ - + + + + diff --git a/java/jni/Application.mk b/java/jni/Application.mk index 3118ec2e4..8b6c80208 100644 --- a/java/jni/Application.mk +++ b/java/jni/Application.mk @@ -1,4 +1,5 @@ APP_ABI := armeabi armeabi-v7a arm64-v8a x86 APP_STL := gnustl_static -APP_CPPFLAGS += -Wall -fPIE -fstack-protector -fexceptions -DZT_TRACE - +APP_CPPFLAGS += -Wall -fPIE -fstack-protector -fexceptions +APP_PLATFORM := android-14 +APP_ABI := all From c9919cc5bae5c06980a701b8a4fa4d31805d4428 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 26 Jun 2015 18:26:57 -0700 Subject: [PATCH 14/27] reflect changes to ZT1_PeerRole in JNI --- java/jni/ZT1_jniutils.cpp | 8 ++++---- java/src/com/zerotier/sdk/PeerRole.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/java/jni/ZT1_jniutils.cpp b/java/jni/ZT1_jniutils.cpp index 6b877417a..90f3bb847 100644 --- a/java/jni/ZT1_jniutils.cpp +++ b/java/jni/ZT1_jniutils.cpp @@ -171,11 +171,11 @@ jobject createPeerRole(JNIEnv *env, ZT1_PeerRole role) case ZT1_PEER_ROLE_LEAF: fieldName = "PEER_ROLE_LEAF"; break; - case ZT1_PEER_ROLE_HUB: - fieldName = "PEER_ROLE_HUB"; + case ZT1_PEER_ROLE_RELAY: + fieldName = "PEER_ROLE_RELAY"; break; - case ZT1_PEER_ROLE_ROOTSERVER: - fieldName = "PEER_ROLE_ROOTSERVER"; + case ZT1_PEER_ROLE_ROOT: + fieldName = "PEER_ROLE_ROOT"; break; } diff --git a/java/src/com/zerotier/sdk/PeerRole.java b/java/src/com/zerotier/sdk/PeerRole.java index 7a5156e1b..d7d55f053 100644 --- a/java/src/com/zerotier/sdk/PeerRole.java +++ b/java/src/com/zerotier/sdk/PeerRole.java @@ -34,12 +34,12 @@ public enum PeerRole { PEER_ROLE_LEAF, /** - * Locally federated hub + * relay node */ - PEER_ROLE_HUB, + PEER_ROLE_RELAY, /** - * planetary rootserver + * root server */ - PEER_ROLE_ROOTSERVER + PEER_ROLE_ROOT } \ No newline at end of file From aee8e95d4961195b3251bcc8581677a8f0c0cdd8 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 30 Jun 2015 19:21:46 -0700 Subject: [PATCH 15/27] logging --- java/jni/com_zerotierone_sdk_Node.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index aa42c5488..ca4c4838c 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -365,7 +365,7 @@ namespace { env->ReleasePrimitiveArrayCritical(objectSizeObj, objSize, 0); } - LOGI("Out Object Size: %lu", *out_objectSize); + LOGV("Out Object Size: %lu", *out_objectSize); return retval; } @@ -410,12 +410,14 @@ namespace { 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) From f803dd5ff46cc03e07364961eb23feb64b7697bd Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 1 Jul 2015 18:13:39 -0700 Subject: [PATCH 16/27] ... --- java/build.xml | 1 + java/jni/Android.mk | 1 + java/jni/com_zerotierone_sdk_Node.cpp | 8 +++++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/java/build.xml b/java/build.xml index 4bc1cdc08..4604ad66e 100644 --- a/java/build.xml +++ b/java/build.xml @@ -42,6 +42,7 @@ + node == node); + if(ref->node != node) + { + LOGE("Nodes not equal. ref->node %p, node %p", ref->node, node); + return; + } JNIEnv *env = NULL; ref->jvm->GetEnv((void**)&env, JNI_VERSION_1_6); @@ -613,6 +617,8 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( &VirtualNetworkConfigFunctionCallback, &EventCallback); + LOGI("Node Created."); + if(rc != ZT1_RESULT_OK) { LOGE("Error creating Node: %d", rc); From 0b7cd2f40a9502a797eff49db12f6f892e319f45 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 1 Jul 2015 20:26:14 -0700 Subject: [PATCH 17/27] change some build settings. add a lock --- java/CMakeLists.txt | 1 + java/jni/Android.mk | 1 + java/jni/Application.mk | 6 +++--- java/jni/com_zerotierone_sdk_Node.cpp | 23 ++++++++++++++++++++--- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt index 382fd3cdb..484517f8b 100644 --- a/java/CMakeLists.txt +++ b/java/CMakeLists.txt @@ -59,6 +59,7 @@ set(src_files set(include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/../include/ + ${CMAKE_CURRENT_SOURCE_DIR}/../node/ ${Java_INCLUDE_DIRS}) if(WIN32) diff --git a/java/jni/Android.mk b/java/jni/Android.mk index 15ba38753..56e6fb80e 100644 --- a/java/jni/Android.mk +++ b/java/jni/Android.mk @@ -4,6 +4,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := ZeroTierOneJNI LOCAL_C_INCLUDES := $(ZT1)/include +LOCAL_C_INCLUDES += $(ZT1)/node LOCAL_LDLIBS := -llog # LOCAL_CFLAGS := -g diff --git a/java/jni/Application.mk b/java/jni/Application.mk index 8b6c80208..0f3697300 100644 --- a/java/jni/Application.mk +++ b/java/jni/Application.mk @@ -1,5 +1,5 @@ -APP_ABI := armeabi armeabi-v7a arm64-v8a x86 -APP_STL := gnustl_static -APP_CPPFLAGS += -Wall -fPIE -fstack-protector -fexceptions +NDK_TOOLCHAIN_VERSION := clang +APP_STL := c++_static +APP_CPPFLAGS := -O2 -fPIE -fvectorize -Wall -fstack-protector -fexceptions -fno-strict-aliasing APP_PLATFORM := android-14 APP_ABI := all diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index e8f7272bd..e98342e7a 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -30,6 +30,7 @@ #include "ZT1_jnilookup.h" #include +#include "Mutex.hpp" #include #include @@ -192,9 +193,9 @@ namespace { { LOGV("EventCallback"); JniRef *ref = (JniRef*)userData; - if(ref->node != node) + if(ref->node != node && event != ZT1_EVENT_UP) { - LOGE("Nodes not equal. ref->node %p, node %p", ref->node, node); + LOGE("Nodes not equal. ref->node %p, node %p. Event: %d", ref->node, node, event); return; } JNIEnv *env = NULL; @@ -478,9 +479,11 @@ namespace { typedef std::map NodeMap; static NodeMap nodeMap; + ZeroTier::Mutex nodeMapMutex; ZT1_Node* findNode(uint64_t nodeId) { + ZeroTier::Mutex::Lock lock(nodeMapMutex); NodeMap::iterator found = nodeMap.find(nodeId); if(found != nodeMap.end()) { @@ -633,8 +636,10 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( return resultObject; } + ZeroTier::Mutex::Lock lock(nodeMapMutex); ref->node = node; nodeMap.insert(std::make_pair(ref->id, ref)); + return resultObject; } @@ -650,7 +655,12 @@ JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete( LOGV("Destroying ZT1_Node struct"); uint64_t nodeId = (uint64_t)id; - NodeMap::iterator found = nodeMap.find(nodeId); + NodeMap::iterator found; + { + ZeroTier::Mutex::Lock lock(nodeMapMutex); + found = nodeMap.find(nodeId); + } + if(found != nodeMap.end()) { JniRef *ref = found->second; @@ -819,6 +829,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( jbyteArray addressArray = (jbyteArray)env->CallObjectMethod(addrObject, getAddressMethod); if(addressArray == NULL) { + LOGE("Unable to call getAddress()"); // unable to call getAddress() return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); } @@ -849,6 +860,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( } else { + LOGE("Unknown IP version"); // unknown address type env->ReleasePrimitiveArrayCritical(addressArray, addr, 0); return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); @@ -856,6 +868,11 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket( env->ReleasePrimitiveArrayCritical(addressArray, addr, 0); unsigned int packetLength = env->GetArrayLength(in_packetData); + if(packetLength == 0) + { + LOGE("Empty packet?!?"); + return createResultObject(env, ZT1_RESULT_FATAL_ERROR_INTERNAL); + } void *packetData = env->GetPrimitiveArrayCritical(in_packetData, NULL); void *localData = malloc(packetLength); memcpy(localData, packetData, packetLength); From ab34884e3e47eb611d42312eb05079119f62e8b8 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Fri, 3 Jul 2015 18:14:50 -0700 Subject: [PATCH 18/27] Set -O0 anything above that currently goes kaboom on ARM platforms --- java/jni/Application.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/jni/Application.mk b/java/jni/Application.mk index 0f3697300..5f68ef6e1 100644 --- a/java/jni/Application.mk +++ b/java/jni/Application.mk @@ -1,5 +1,5 @@ NDK_TOOLCHAIN_VERSION := clang APP_STL := c++_static -APP_CPPFLAGS := -O2 -fPIE -fvectorize -Wall -fstack-protector -fexceptions -fno-strict-aliasing +APP_CPPFLAGS := -O0 -fPIC -fPIE -fvectorize -Wall -fstack-protector -fexceptions -fno-strict-aliasing APP_PLATFORM := android-14 APP_ABI := all From a408e5f6858a0e2f13c27e56f203b35f64ab07e7 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Wed, 8 Jul 2015 17:59:53 -0700 Subject: [PATCH 19/27] set ZT_NO_TYPE_PUNNING and -O3 for Android builds --- java/jni/Application.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/jni/Application.mk b/java/jni/Application.mk index 5f68ef6e1..2a7897870 100644 --- a/java/jni/Application.mk +++ b/java/jni/Application.mk @@ -1,5 +1,5 @@ NDK_TOOLCHAIN_VERSION := clang APP_STL := c++_static -APP_CPPFLAGS := -O0 -fPIC -fPIE -fvectorize -Wall -fstack-protector -fexceptions -fno-strict-aliasing +APP_CPPFLAGS := -O3 -fPIC -fPIE -fvectorize -Wall -fstack-protector -fexceptions -fno-strict-aliasing -DZT_NO_TYPE_PUNNING=1 APP_PLATFORM := android-14 APP_ABI := all From e45475c5b5bcee4fb88e797a50eb38b6ad3d6923 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 9 Jul 2015 20:26:23 -0700 Subject: [PATCH 20/27] fixed a misspelling --- java/src/com/zerotier/sdk/EventListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/com/zerotier/sdk/EventListener.java b/java/src/com/zerotier/sdk/EventListener.java index 078023d95..bb191c1d5 100644 --- a/java/src/com/zerotier/sdk/EventListener.java +++ b/java/src/com/zerotier/sdk/EventListener.java @@ -42,7 +42,7 @@ public interface EventListener { public void onEvent(Event event); /** - * Callback for network error events: {@link Event.EVENT_AUTHENTICATION_FAILUER}, {link Event.EVENT_INVALID_PACKET} + * Callback for network error events: {@link Event.EVENT_AUTHENTICATION_FAILURE}, {link Event.EVENT_INVALID_PACKET} * * @param event {@link Event} enum * @param source {@link InetSocketAddress} containing the origin address of the packet From d5b48c90edd801f41a40783312deca9f233605ae Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Thu, 27 Aug 2015 19:51:27 -0700 Subject: [PATCH 21/27] added a 512x512 app icon (for Play store) --- artwork/ZeroTierIcon512x512.png | Bin 0 -> 51309 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 artwork/ZeroTierIcon512x512.png diff --git a/artwork/ZeroTierIcon512x512.png b/artwork/ZeroTierIcon512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..d225c2e339de78c4e4ba15c802cb7ae51f4222e4 GIT binary patch literal 51309 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 literal 0 HcmV?d00001 From 73bedfcc016e69b682dc916d26490c3810482ce0 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sat, 29 Aug 2015 17:54:30 -0700 Subject: [PATCH 22/27] ifdef default assignments --- include/ZeroTierOne.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 8583aa3ad..b8d14c5ff 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -760,7 +760,11 @@ enum ZT1_ResultCode ZT1_Node_new( ZT1_VirtualNetworkFrameFunction virtualNetworkFrameFunction, ZT1_VirtualNetworkConfigFunction virtualNetworkConfigFunction, ZT1_EventCallback eventCallback, - const char *overrideRootTopology = (const char *)0); + const char *overrideRootTopology +#ifdef __cplusplus + = (const char *)0 +#endif + ); /** * Delete a node and free all resources it consumes @@ -881,7 +885,11 @@ enum ZT1_ResultCode ZT1_Node_leave(ZT1_Node *node,uint64_t nwid); * @param multicastAdi Multicast ADI (least significant 32 bits only, default: 0) * @return OK (0) or error code if a fatal error condition has occurred */ -enum ZT1_ResultCode ZT1_Node_multicastSubscribe(ZT1_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi = 0); +enum ZT1_ResultCode ZT1_Node_multicastSubscribe(ZT1_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi +#ifdef __cplusplus + = 0 +#endif + ); /** * Unsubscribe from an Ethernet multicast group (or all groups) @@ -897,7 +905,11 @@ enum ZT1_ResultCode ZT1_Node_multicastSubscribe(ZT1_Node *node,uint64_t nwid,uin * @param multicastAdi Multicast ADI (least significant 32 bits only, default: 0) * @return OK (0) or error code if a fatal error condition has occurred */ -enum ZT1_ResultCode ZT1_Node_multicastUnsubscribe(ZT1_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi = 0); +enum ZT1_ResultCode ZT1_Node_multicastUnsubscribe(ZT1_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi +#ifdef __cplusplus + = 0 +#endif + ); /** * Get this node's 40-bit ZeroTier address From 0b9e5928d35c43748d8cd413e05e5718f7b1fc52 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sat, 26 Sep 2015 13:53:38 -0700 Subject: [PATCH 23/27] update PacketSender interface --- java/src/com/zerotier/sdk/PacketSender.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/java/src/com/zerotier/sdk/PacketSender.java b/java/src/com/zerotier/sdk/PacketSender.java index 5302f5ce5..ab31729be 100644 --- a/java/src/com/zerotier/sdk/PacketSender.java +++ b/java/src/com/zerotier/sdk/PacketSender.java @@ -37,11 +37,13 @@ public interface PacketSender { * on failure. Note that success does not (of course) guarantee packet * delivery. It only means that the packet appears to have been sent.

* - * @param addr {@link InetSocketAddress} to send to + * @param localAddr {@link InetSocketAddress} to send from. Set to null if not specified. + * @param remoteAddr {@link InetSocketAddress} to send to * @param packetData data to send * @return 0 on success, any error code on failure. */ public int onSendPacketRequested( - InetSocketAddress addr, + InetSocketAddress localAddr, + InetSocketAddress remoteAddr, byte[] packetData); } From 7c3be2b5c152e8631237c1050e0c90ad37dfcb1d Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sat, 26 Sep 2015 14:10:16 -0700 Subject: [PATCH 24/27] fix function signature in lookup for onSendPacketRequested function --- java/jni/com_zerotierone_sdk_Node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index e513cdf52..e1b69d1e4 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -462,7 +462,7 @@ namespace { } jmethodID packetSenderCallbackMethod = lookup.findMethod(packetSenderClass, - "onSendPacketRequested", "(Ljava/net/InetSocketAddress;[B)I"); + "onSendPacketRequested", "(Ljava/net/InetSocketAddress;Ljava/net/InetSocketAddress;[B)I"); if(packetSenderCallbackMethod == NULL) { LOGE("Couldn't find onSendPacketRequested method"); From 75a191a8564030f4d5e99aca76b980e2d69abd20 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Sat, 26 Sep 2015 14:10:45 -0700 Subject: [PATCH 25/27] don't create an InetSocketAddress on local address if it's equal to ZT_SOCKADDR_NULL --- java/jni/com_zerotierone_sdk_Node.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index e1b69d1e4..e60426491 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -469,7 +469,12 @@ namespace { return -2; } - jobject localAddressObj = newInetSocketAddress(env, *localAddress); + 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); From b7df177f33fd7a98da18ed9f756ee7272982a65d Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Mon, 2 Nov 2015 19:18:55 -0800 Subject: [PATCH 26/27] updates for origin/edge --- java/jni/Android.mk | 2 +- java/jni/Application.mk | 2 +- java/jni/ZT_jniutils.cpp | 18 ------ java/jni/com_zerotierone_sdk_Node.cpp | 64 ++++---------------- java/src/com/zerotier/sdk/Event.java | 25 -------- java/src/com/zerotier/sdk/EventListener.java | 15 ----- 6 files changed, 15 insertions(+), 111 deletions(-) diff --git a/java/jni/Android.mk b/java/jni/Android.mk index 22dfe4f15..3da3b04b6 100644 --- a/java/jni/Android.mk +++ b/java/jni/Android.mk @@ -15,7 +15,6 @@ LOCAL_SRC_FILES := \ $(ZT1)/ext/http-parser/http_parser.c \ $(ZT1)/node/C25519.cpp \ $(ZT1)/node/CertificateOfMembership.cpp \ - $(ZT1)/node/Defaults.cpp \ $(ZT1)/node/Dictionary.cpp \ $(ZT1)/node/Identity.cpp \ $(ZT1)/node/IncomingPacket.cpp \ @@ -26,6 +25,7 @@ LOCAL_SRC_FILES := \ $(ZT1)/node/Node.cpp \ $(ZT1)/node/OutboundMulticast.cpp \ $(ZT1)/node/Packet.cpp \ + $(ZT1)/node/Path.cpp \ $(ZT1)/node/Peer.cpp \ $(ZT1)/node/Poly1305.cpp \ $(ZT1)/node/Salsa20.cpp \ diff --git a/java/jni/Application.mk b/java/jni/Application.mk index 2a7897870..608e94c0a 100644 --- a/java/jni/Application.mk +++ b/java/jni/Application.mk @@ -1,5 +1,5 @@ NDK_TOOLCHAIN_VERSION := clang APP_STL := c++_static -APP_CPPFLAGS := -O3 -fPIC -fPIE -fvectorize -Wall -fstack-protector -fexceptions -fno-strict-aliasing -DZT_NO_TYPE_PUNNING=1 +APP_CPPFLAGS := -O3 -fPIC -fPIE -fvectorize -Wall -fstack-protector -fexceptions -fno-strict-aliasing -Wno-deprecated-register -DZT_NO_TYPE_PUNNING=1 APP_PLATFORM := android-14 APP_ABI := all diff --git a/java/jni/ZT_jniutils.cpp b/java/jni/ZT_jniutils.cpp index bd8d87084..ae1aa9298 100644 --- a/java/jni/ZT_jniutils.cpp +++ b/java/jni/ZT_jniutils.cpp @@ -133,15 +133,6 @@ jobject createEvent(JNIEnv *env, ZT_Event event) case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION: fieldName = "EVENT_FATAL_ERROR_IDENTITY_COLLISION"; break; - case ZT_EVENT_SAW_MORE_RECENT_VERSION: - fieldName = "EVENT_SAW_MORE_RECENT_VERSION"; - break; - case ZT_EVENT_AUTHENTICATION_FAILURE: - fieldName = "EVENT_AUTHENTICATION_FAILURE"; - break; - case ZT_EVENT_INVALID_PACKET: - fieldName = "EVENT_INVALID_PACKET"; - break; case ZT_EVENT_TRACE: fieldName = "EVENT_TRACE"; break; @@ -425,7 +416,6 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp) jfieldID addressField = NULL; jfieldID lastSendField = NULL; jfieldID lastReceiveField = NULL; - jfieldID fixedField = NULL; jfieldID activeField = NULL; jfieldID preferredField = NULL; @@ -459,13 +449,6 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp) return NULL; } - fixedField = lookup.findField(pppClass, "fixed", "Z"); - if(env->ExceptionCheck() || fixedField == NULL) - { - LOGE("Error finding fixed field"); - return NULL; - } - activeField = lookup.findField(pppClass, "active", "Z"); if(env->ExceptionCheck() || activeField == NULL) { @@ -503,7 +486,6 @@ jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp) env->SetObjectField(pppObject, addressField, addressObject); env->SetLongField(pppObject, lastSendField, ppp.lastSend); env->SetLongField(pppObject, lastReceiveField, ppp.lastReceive); - env->SetBooleanField(pppObject, fixedField, ppp.fixed); env->SetBooleanField(pppObject, activeField, ppp.active); env->SetBooleanField(pppObject, preferredField, ppp.preferred); diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index e60426491..a4c677b7c 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -134,7 +134,8 @@ namespace { (jlong)nwid, operationObject, networkConfigObject); } - void VirtualNetworkFrameFunctionCallback(ZT_Node *node,void *userData, + void VirtualNetworkFrameFunctionCallback(ZT_Node *node, + void *userData, uint64_t nwid, uint64_t sourceMac, uint64_t destMac, @@ -189,7 +190,10 @@ namespace { } - void EventCallback(ZT_Node *node,void *userData,enum ZT_Event event, const void *data) + void EventCallback(ZT_Node *node, + void *userData, + enum ZT_Event event, + const void *data) { LOGV("EventCallback"); JniRef *ref = (JniRef*)userData; @@ -217,25 +221,6 @@ namespace { return; } - - jmethodID onOutOfDateMethod = lookup.findMethod(eventListenerClass, - "onOutOfDate", "(Lcom/zerotier/sdk/Version;)V"); - if(onOutOfDateMethod == NULL) - { - LOGE("Couldn't find onOutOfDate method"); - return; - } - - - jmethodID onNetworkErrorMethod = lookup.findMethod(eventListenerClass, - "onNetworkError", "(Lcom/zerotier/sdk/Event;Ljava/net/InetSocketAddress;)V"); - if(onNetworkErrorMethod == NULL) - { - LOGE("Couldn't find onNetworkError method"); - return; - } - - jmethodID onTraceMethod = lookup.findMethod(eventListenerClass, "onTrace", "(Ljava/lang/String;)V"); if(onTraceMethod == NULL) @@ -263,31 +248,6 @@ namespace { env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject); } break; - case ZT_EVENT_SAW_MORE_RECENT_VERSION: - { - LOGV("Version Event"); - // call onOutOfDate() - if(data != NULL) - { - int *version = (int*)data; - jobject verisonObj = newVersion(env, version[0], version[1], version[2], 0); - env->CallVoidMethod(ref->eventListener, onOutOfDateMethod, verisonObj); - } - } - break; - case ZT_EVENT_AUTHENTICATION_FAILURE: - case ZT_EVENT_INVALID_PACKET: - { - LOGV("Network Error Event"); - // call onNetworkError() - if(data != NULL) - { - sockaddr_storage *addr = (sockaddr_storage*)data; - jobject addressObj = newInetSocketAddress(env, *addr); - env->CallVoidMethod(ref->eventListener, onNetworkErrorMethod, addressObj); - } - } - break; case ZT_EVENT_TRACE: { LOGV("Trace Event"); @@ -303,7 +263,8 @@ namespace { } } - long DataStoreGetFunction(ZT_Node *node,void *userData, + long DataStoreGetFunction(ZT_Node *node, + void *userData, const char *objectName, void *buffer, unsigned long bufferSize, @@ -375,7 +336,8 @@ namespace { return retval; } - int DataStorePutFunction(ZT_Node *node,void *userData, + int DataStorePutFunction(ZT_Node *node, + void *userData, const char *objectName, const void *buffer, unsigned long bufferSize, @@ -440,7 +402,8 @@ namespace { } } - int WirePacketSendFunction(ZT_Node *node,void *userData,\ + int WirePacketSendFunction(ZT_Node *node, + void *userData, const struct sockaddr_storage *localAddress, const struct sockaddr_storage *remoteAddress, const void *buffer, @@ -625,8 +588,7 @@ JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init( &WirePacketSendFunction, &VirtualNetworkFrameFunctionCallback, &VirtualNetworkConfigFunctionCallback, - &EventCallback, - NULL); + &EventCallback); if(rc != ZT_RESULT_OK) { diff --git a/java/src/com/zerotier/sdk/Event.java b/java/src/com/zerotier/sdk/Event.java index 436b1b226..22d350e1e 100644 --- a/java/src/com/zerotier/sdk/Event.java +++ b/java/src/com/zerotier/sdk/Event.java @@ -86,31 +86,6 @@ public enum Event { * umbrellas prevent rain and smoke detectors prevent fires. They do, right?

*/ EVENT_FATAL_ERROR_IDENTITY_COLLISION, - - /** - * A more recent version was observed on the network - * - *

Right now this is only triggered if a hub or rootserver reports a - * more recent version, and only once. It can be used to trigger a - * software update check.

- * - *

Meta-data: {@link Version}, more recent version number

- */ - EVENT_SAW_MORE_RECENT_VERSION, - - /** - * A packet failed authentication - * - *

Meta-data: {@link InetSocketAddress} containing origin address of packet

- */ - EVENT_AUTHENTICATION_FAILURE, - - /** - * A received packet was not valid - * - *

Meta-data: {@link InetSocketAddress} containing origin address of packet

- */ - EVENT_INVALID_PACKET, /** * Trace (debugging) message diff --git a/java/src/com/zerotier/sdk/EventListener.java b/java/src/com/zerotier/sdk/EventListener.java index bb191c1d5..91050aaa9 100644 --- a/java/src/com/zerotier/sdk/EventListener.java +++ b/java/src/com/zerotier/sdk/EventListener.java @@ -41,21 +41,6 @@ public interface EventListener { */ public void onEvent(Event event); - /** - * Callback for network error events: {@link Event.EVENT_AUTHENTICATION_FAILURE}, {link Event.EVENT_INVALID_PACKET} - * - * @param event {@link Event} enum - * @param source {@link InetSocketAddress} containing the origin address of the packet - */ - public void onNetworkError(Event event, InetSocketAddress source); - - /** - * Callback when the node detects that it's out of date. - * - * @param newVersion {@link Version} object with the latest version of ZeroTier One - */ - public void onOutOfDate(Version newVersion); - /** * Trace messages * From eadeac0a42888a6f2fa53e2a802c6c4e43c055b3 Mon Sep 17 00:00:00 2001 From: Grant Limberg Date: Tue, 3 Nov 2015 19:14:11 -0800 Subject: [PATCH 27/27] logging of events --- java/jni/com_zerotierone_sdk_Node.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/java/jni/com_zerotierone_sdk_Node.cpp b/java/jni/com_zerotierone_sdk_Node.cpp index a4c677b7c..17a9917a9 100644 --- a/java/jni/com_zerotierone_sdk_Node.cpp +++ b/java/jni/com_zerotierone_sdk_Node.cpp @@ -238,12 +238,32 @@ namespace { 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("Regular Event"); + LOGV("Identity Collision"); // call onEvent() env->CallVoidMethod(ref->eventListener, onEventMethod, eventObject); }