updated
63
integrations/Android/android_zt_sdk.md
Normal file
@@ -0,0 +1,63 @@
|
||||
Android + ZeroTier SDK
|
||||
====
|
||||
|
||||
Welcome!
|
||||
|
||||
Imagine a flat, encrypted, no-configuration LAN for all of the instances of your Android app.
|
||||
|
||||
This short tutorial will show you how to enable ZeroTier functionality for your Android app with little to no code modification. Check out our [ZeroTier SDK](https://www.zerotier.com/blog) page for more info on how the integration works and [Shim Techniques](https://www.zerotier.com/blog) for a discussion of shims available for your app/technology.
|
||||
|
||||
In this example we aim to set up a minimal [Android Studio](https://developer.android.com/studio/index.html) project which contains all of the components necessary to enable ZeroTier for your app. If you'd rather skip all of these steps and grab the code, look in the [sdk/android](https://github.com/zerotier/ZeroTierOne/tree/dev/netcon/Android) folder in the source tree. Otherwise, let's get started!
|
||||
|
||||
**Step 1: Build Shared Library `libZeroTierOneJNI.so`**
|
||||
|
||||
Open `ZeroTierOne/Netcon/Android/proj` and build it.
|
||||
|
||||
*Note: Building the project will take a while if you are building for all architectures, See note below on how to speed up this process.*
|
||||
|
||||
The resultant `ZeroTierOne/netcon/Android/java/libs/YOUR_ARCH/libZeroTierOneJNI.so` will be what you want to import for your own project to provide the shim interface to your app. Select your architecture and copy the shared library into `YourProject/src/main/jniLibs/YOUR_ARCH/`
|
||||
|
||||
**Step 2: App Code Modifications**
|
||||
|
||||
Create new package called `Netcon` in your project and add a new file called `NetconWrapper.java` containing:
|
||||
|
||||
```
|
||||
package Netcon;
|
||||
public class NetconWrapper {
|
||||
public native void startOneService();
|
||||
static { System.loadLibrary("ZeroTierOneJNI”); } // Loads JNI code
|
||||
}
|
||||
```
|
||||
|
||||
And now, start the service:
|
||||
```
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
NetconWrapper wrapper = new NetconWrapper();
|
||||
wrapper.startOneService(); // Calls to JNI code
|
||||
}
|
||||
}).start();
|
||||
```
|
||||
**Step 3: Pick a shim for your app**
|
||||
|
||||
If functional interposition isn't available for the API or library you've chosen to use, ZeroTier offers a SOCKS5 proxy server which can allow connectivity to your virtual network as long as your client API supports the SOCKS5 protocol. This proxy service will run alongside the tap service and can be turned on by compiling with the `-DUSE_SOCKS_PROXY` flag. By default, the proxy service is available at `0.0.0.0:1337`.
|
||||
|
||||
**Step 4: Add necessary app permissions**
|
||||
|
||||
In order for your application to write the auth keys to the internal storage you'll need to set a few permissions in your `AndroidManifest.xml` file:
|
||||
|
||||
```
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
```
|
||||
|
||||
**Step 5: Join a network!**
|
||||
|
||||
Simply call `zt_join_network("XXXXXXXXXXXXXXXX")`
|
||||
|
||||
***
|
||||
**Additional notes**
|
||||
|
||||
As mentioned above, you can reduce the amount of time required to build the ZeroTier JNI library by only building for the architectures you want. You can specify the architectures in `ZeroTierOne/netcon/Android/java/jni/Application.mk`
|
||||
|
||||
If you change the method/class/package name for the Netcon glue code in `NetconWrapper.java` (Not recommended!), you must also change the name of the JNI implementation in the Netcon source to match the new java name. For example, if the glue code is contained in a package `Java.com.example.joseph.NetconProxyTest`, a JNI implementation name of `Java_com_example_joseph_netconproxytest_NetconWrapper_startOneService` would be required in the appropriate C/C++ source/header files.
|
||||
17
integrations/Android/java/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
ZeroTier One SDK - Android JNI Wrapper
|
||||
=====
|
||||
|
||||
|
||||
Building
|
||||
-----
|
||||
|
||||
Reqires:
|
||||
|
||||
* JDK
|
||||
* ANT
|
||||
* Android NDK
|
||||
|
||||
Required Environment Variables:
|
||||
|
||||
* NDK\_BUILD\_LOC - Path do the ndk-build script in the Android NDK
|
||||
* ANDROID\_PLATFORM - path to the directory android.jar lives (on Windows: C:\Users\<username>\AppData\Local\Android\sdk\platforms\android-21)
|
||||
118
integrations/Android/java/build.xml
Normal file
@@ -0,0 +1,118 @@
|
||||
<project default="build_jar" name="ZeroTierOneSDK" basedir=".">
|
||||
<property environment="env"/>
|
||||
|
||||
<condition property="isWindows">
|
||||
<os family="windows"/>
|
||||
</condition>
|
||||
|
||||
<condition property="isMac">
|
||||
<os family="mac"/>
|
||||
</condition>
|
||||
|
||||
<target name="clean_ant">
|
||||
<delete dir="bin" failonerror="false"/>
|
||||
<delete dir="classes" failonerror="false"/>
|
||||
<delete dir="build_win32" failonerror="false"/>
|
||||
<delete dir="build_win64" failonerror="false"/>
|
||||
<delete dir="mac32_64" failonerror="false"/>
|
||||
<delete dir="libs" failonerror="false"/>
|
||||
<delete dir="obj" failonerror="false"/>
|
||||
</target>
|
||||
|
||||
<target name="build_java">
|
||||
<echo message="os.name = ${os.name}"/>
|
||||
<echo message="os.arch = ${os.arch}"/>
|
||||
<echo message="ant.java.version = ${ant.java.version}"/>
|
||||
<echo message="java.version = ${java.version}"/>
|
||||
<echo message="ndk.loc = ${env.NDK_BUILD_LOC}"/>
|
||||
<echo message="sdk.loc = ${env.ANDROID_PLATFORM}"/>
|
||||
<echo message="user.dir = ${user.dir}"/>
|
||||
<echo message="zt1.dir = ${env.ZT}"/>
|
||||
<mkdir dir="bin"/>
|
||||
<mkdir dir="classes"/>
|
||||
<javac srcdir="src"
|
||||
destdir="classes"
|
||||
source="1.7"
|
||||
target="1.7"
|
||||
classpath="${env.ANDROID_PLATFORM}/android.jar"
|
||||
includeantruntime="false"/>
|
||||
</target>
|
||||
|
||||
<target name="build_android">
|
||||
<exec dir="jni" executable="${env.NDK_BUILD_LOC}" failonerror="true">
|
||||
<arg value="ZT1=${env.ZT}"/>
|
||||
<arg value="V=1"/>
|
||||
<!-- <arg value="NDK_DEBUG=1"/> -->
|
||||
</exec>
|
||||
<copy file="libs/arm64-v8a/libZeroTierOneJNI.so"
|
||||
tofile="classes/lib/arm64-v8a/libZeroTierOneJNI.so"
|
||||
overwrite="true"/>
|
||||
<copy file="libs/armeabi/libZeroTierOneJNI.so"
|
||||
tofile="classes/lib/armeabi/libZeroTierOneJNI.so"
|
||||
overwrite="true"/>
|
||||
<copy file="libs/armeabi-v7a/libZeroTierOneJNI.so"
|
||||
tofile="classes/lib/armeabi-v7a/libZeroTierOneJNI.so"
|
||||
overwrite="true"/>
|
||||
<copy file="libs/mips/libZeroTierOneJNI.so"
|
||||
tofile="classes/lib/mips/libZeroTierOneJNI.so"
|
||||
overwrite="true"/>
|
||||
<copy file="libs/mips64/libZeroTierOneJNI.so"
|
||||
tofile="classes/lib/mips64/libZeroTierOne.so"
|
||||
overwrite="true"/>
|
||||
<copy file="libs/x86/libZeroTierOneJNI.so"
|
||||
tofile="classes/lib/x86/libZeroTierOneJNI.so"
|
||||
overwrite="true"/>
|
||||
<copy file="libs/x86_64/libZeroTierOneJNI.so"
|
||||
tofile="classes/lib/x86_64/libZeroTierOneJNI.so"
|
||||
overwrite="true"/>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="windows" if="isWindows">
|
||||
<mkdir dir="build_win32"/>
|
||||
<exec dir="build_win32/" executable="cmake" failonerror="true">
|
||||
<arg line=".. -G"Visual Studio 11 2012" -DCMAKE_BUILD_TYPE=Release"/>
|
||||
</exec>
|
||||
<exec dir="build_win32/" executable="cmake" failonerror="true">
|
||||
<arg line="--build . --config Release"/>
|
||||
</exec>
|
||||
<copy file="build_win32/Release/ZeroTierOneJNI.dll"
|
||||
tofile="classes/lib/ZeroTierOneJNI_win32.dll"
|
||||
overwrite="true"/>
|
||||
|
||||
<mkdir dir="build_win64"/>
|
||||
<exec dir="build_win64/" executable="cmake" failonerror="true">
|
||||
<arg line=".. -G"Visual Studio 11 2012 Win64" -DCMAKE_BUILD_TYPE=Release"/>
|
||||
</exec>
|
||||
<exec dir="build_win64/" executable="cmake" failonerror="true">
|
||||
<arg line="--build . --config Release"/>
|
||||
</exec>
|
||||
<copy file="build_win64/Release/ZeroTierOneJNI.dll"
|
||||
tofile="classes/lib/ZeroTierOneJNI_win64.dll"
|
||||
overwrite="true"/>
|
||||
</target>
|
||||
|
||||
<target name="mac" if="isMac">
|
||||
<mkdir dir="mac32_64"/>
|
||||
<exec dir="mac32_64/" executable="cmake" failonerror="true">
|
||||
<arg line=".. -DCMAKE_BUILD_TYPE=Release"/>
|
||||
</exec>
|
||||
<exec dir="mac32_64/" executable="cmake" failonerror="true">
|
||||
<arg line="--build . --config Release"/>
|
||||
</exec>
|
||||
<copy file="mac32_64/libZeroTierOneJNI.jnilib"
|
||||
tofile="classes/lib/libZeroTierOneJNI.jnilib"
|
||||
overwrite="true"/>
|
||||
</target>
|
||||
|
||||
<target name="build_jar" depends="build_java,build_android,windows,mac">
|
||||
<jar destfile="bin/ZeroTierOneSDK.jar" basedir="classes"/>
|
||||
</target>
|
||||
|
||||
<target name="docs">
|
||||
<echo message="Generating Javadocs"/>
|
||||
<mkdir dir="doc/"/>
|
||||
<javadoc sourcepath="src/"
|
||||
destdir="doc/"/>
|
||||
</target>
|
||||
</project>
|
||||
113
integrations/Android/java/jni/Android.mk
Normal file
@@ -0,0 +1,113 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := ZeroTierOneJNI
|
||||
LOCAL_C_INCLUDES := $(ZT1)/include
|
||||
LOCAL_C_INCLUDES += $(ZT1)/ext/lwip/src/include
|
||||
LOCAL_C_INCLUDES += $(ZT1)/ext/lwip/src/include/ipv4
|
||||
LOCAL_C_INCLUDES += $(ZT1)/netcon
|
||||
LOCAL_C_INCLUDES += $(ZT1)/service
|
||||
LOCAL_C_INCLUDES += $(ZT1)/osdep
|
||||
LOCAL_C_INCLUDES += $(ZT1)/node
|
||||
|
||||
LOCAL_LDLIBS := -llog
|
||||
# LOCAL_CFLAGS := -g
|
||||
|
||||
# Netcon files
|
||||
LOCAL_SRC_FILES := \
|
||||
$(ZT1)/netcon/NetconRPC.c \
|
||||
$(ZT1)/netcon/NetconProxy.cpp \
|
||||
$(ZT1)/netcon/NetconServiceSetup.cpp \
|
||||
$(ZT1)/netcon/NetconEthernetTap.cpp
|
||||
|
||||
# ZeroTierOne ext files
|
||||
LOCAL_SRC_FILES += \
|
||||
$(ZT1)/ext/lz4/lz4.c \
|
||||
$(ZT1)/ext/json-parser/json.c \
|
||||
$(ZT1)/ext/http-parser/http_parser.c \
|
||||
|
||||
# ZeroTierOne files
|
||||
LOCAL_SRC_FILES += \
|
||||
$(ZT1)/service/OneService.cpp \
|
||||
$(ZT1)/service/ControlPlane.cpp \
|
||||
$(ZT1)/node/C25519.cpp \
|
||||
$(ZT1)/node/CertificateOfMembership.cpp \
|
||||
$(ZT1)/node/DeferredPackets.cpp \
|
||||
$(ZT1)/node/Dictionary.cpp \
|
||||
$(ZT1)/node/Identity.cpp \
|
||||
$(ZT1)/node/IncomingPacket.cpp \
|
||||
$(ZT1)/node/InetAddress.cpp \
|
||||
$(ZT1)/node/Multicaster.cpp \
|
||||
$(ZT1)/node/Network.cpp \
|
||||
$(ZT1)/node/NetworkConfig.cpp \
|
||||
$(ZT1)/node/Node.cpp \
|
||||
$(ZT1)/node/OutboundMulticast.cpp \
|
||||
$(ZT1)/node/Packet.cpp \
|
||||
$(ZT1)/node/Path.cpp \
|
||||
$(ZT1)/node/Peer.cpp \
|
||||
$(ZT1)/node/Poly1305.cpp \
|
||||
$(ZT1)/node/Salsa20.cpp \
|
||||
$(ZT1)/node/SelfAwareness.cpp \
|
||||
$(ZT1)/node/SHA512.cpp \
|
||||
$(ZT1)/node/Switch.cpp \
|
||||
$(ZT1)/node/Topology.cpp \
|
||||
$(ZT1)/node/Utils.cpp \
|
||||
$(ZT1)/osdep/Http.cpp \
|
||||
$(ZT1)/osdep/OSUtils.cpp \
|
||||
$(ZT1)/osdep/BackgroundResolver.cpp
|
||||
|
||||
# lwIP api files
|
||||
LOCAL_SRC_FILES += \
|
||||
$(ZT1)/ext/lwip/src/api/api_lib.c \
|
||||
$(ZT1)/ext/lwip/src/api/api_msg.c \
|
||||
$(ZT1)/ext/lwip/src/api/err.c \
|
||||
$(ZT1)/ext/lwip/src/api/netbuf.c \
|
||||
$(ZT1)/ext/lwip/src/api/netdb.c \
|
||||
$(ZT1)/ext/lwip/src/api/netifapi.c \
|
||||
$(ZT1)/ext/lwip/src/api/sockets.c \
|
||||
$(ZT1)/ext/lwip/src/api/tcpip.c
|
||||
|
||||
# lwIP core files
|
||||
LOCAL_SRC_FILES += \
|
||||
$(ZT1)/ext/lwip/src/core/def.c \
|
||||
$(ZT1)/ext/lwip/src/core/dhcp.c \
|
||||
$(ZT1)/ext/lwip/src/core/dns.c \
|
||||
$(ZT1)/ext/lwip/src/core/init.c \
|
||||
$(ZT1)/ext/lwip/src/core/mem.c \
|
||||
$(ZT1)/ext/lwip/src/core/memp.c \
|
||||
$(ZT1)/ext/lwip/src/core/netif.c \
|
||||
$(ZT1)/ext/lwip/src/core/pbuf.c \
|
||||
$(ZT1)/ext/lwip/src/core/raw.c \
|
||||
$(ZT1)/ext/lwip/src/core/stats.c \
|
||||
$(ZT1)/ext/lwip/src/core/sys.c \
|
||||
$(ZT1)/ext/lwip/src/core/tcp_in.c \
|
||||
$(ZT1)/ext/lwip/src/core/tcp_out.c \
|
||||
$(ZT1)/ext/lwip/src/core/tcp.c \
|
||||
$(ZT1)/ext/lwip/src/core/timers.c \
|
||||
$(ZT1)/ext/lwip/src/core/udp.c
|
||||
|
||||
# lwIP core/ip4 files
|
||||
LOCAL_SRC_FILES += \
|
||||
$(ZT1)/ext/lwip/src/core/ipv4/autoip.c \
|
||||
$(ZT1)/ext/lwip/src/core/ipv4/icmp.c \
|
||||
$(ZT1)/ext/lwip/src/core/ipv4/igmp.c \
|
||||
$(ZT1)/ext/lwip/src/core/ipv4/inet_chksum.c \
|
||||
$(ZT1)/ext/lwip/src/core/ipv4/inet.c \
|
||||
$(ZT1)/ext/lwip/src/core/ipv4/ip_addr.c \
|
||||
$(ZT1)/ext/lwip/src/core/ipv4/ip_frag.c \
|
||||
$(ZT1)/ext/lwip/src/core/ipv4/ip.c \
|
||||
|
||||
# lwIP netif files
|
||||
LOCAL_SRC_FILES += \
|
||||
$(ZT1)/ext/lwip/src/netif/etharp.c \
|
||||
$(ZT1)/ext/lwip/src/netif/ethernetif.c \
|
||||
$(ZT1)/ext/lwip/src/netif/slipif.c
|
||||
|
||||
# JNI Files
|
||||
LOCAL_SRC_FILES += \
|
||||
com_zerotierone_sdk_Node.cpp \
|
||||
ZT_jniutils.cpp \
|
||||
ZT_jnilookup.cpp
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
8
integrations/Android/java/jni/Application.mk
Normal file
@@ -0,0 +1,8 @@
|
||||
NDK_TOOLCHAIN_VERSION := clang
|
||||
APP_STL := c++_static
|
||||
APP_CPPFLAGS := -O3 -DUSE_SOCKS_PROXY -DZT_SERVICE_NETCON -fPIC -fPIE -fvectorize -Wall -fstack-protector -fexceptions -fno-strict-aliasing -Wno-deprecated-register -DZT_NO_TYPE_PUNNING=1
|
||||
APP_PLATFORM := android-14
|
||||
|
||||
# Architectures
|
||||
#APP_ABI := all
|
||||
APP_ABI := armeabi-v7a
|
||||
158
integrations/Android/java/jni/ZT_jnilookup.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
#include "ZT_jnilookup.h"
|
||||
#include "ZT_jniutils.h"
|
||||
|
||||
JniLookup::JniLookup()
|
||||
: m_jvm(NULL)
|
||||
{
|
||||
LOGV("JNI Cache Created");
|
||||
}
|
||||
|
||||
JniLookup::JniLookup(JavaVM *jvm)
|
||||
: m_jvm(jvm)
|
||||
{
|
||||
LOGV("JNI Cache Created");
|
||||
}
|
||||
|
||||
JniLookup::~JniLookup()
|
||||
{
|
||||
LOGV("JNI Cache Destroyed");
|
||||
}
|
||||
|
||||
|
||||
void JniLookup::setJavaVM(JavaVM *jvm)
|
||||
{
|
||||
LOGV("Assigned JVM to object");
|
||||
m_jvm = jvm;
|
||||
}
|
||||
|
||||
|
||||
jclass JniLookup::findClass(const std::string &name)
|
||||
{
|
||||
if(!m_jvm)
|
||||
return NULL;
|
||||
|
||||
// get the class from the JVM
|
||||
JNIEnv *env = NULL;
|
||||
if(m_jvm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK)
|
||||
{
|
||||
LOGE("Error 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;
|
||||
}
|
||||
54
integrations/Android/java/jni/ZT_jnilookup.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
#ifndef ZT_JNILOOKUP_H_
|
||||
#define ZT_JNILOOKUP_H_
|
||||
|
||||
#include <jni.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
|
||||
class JniLookup {
|
||||
public:
|
||||
JniLookup();
|
||||
JniLookup(JavaVM *jvm);
|
||||
~JniLookup();
|
||||
|
||||
void setJavaVM(JavaVM *jvm);
|
||||
|
||||
jclass findClass(const std::string &name);
|
||||
jmethodID findMethod(jclass cls, const std::string &methodName, const std::string &methodSig);
|
||||
jmethodID findStaticMethod(jclass cls, const std::string &methodName, const std::string &methodSig);
|
||||
jfieldID findField(jclass cls, const std::string &fieldName, const std::string &typeStr);
|
||||
jfieldID findStaticField(jclass cls, const std::string &fieldName, const std::string &typeStr);
|
||||
private:
|
||||
JavaVM *m_jvm;
|
||||
};
|
||||
|
||||
#endif
|
||||
931
integrations/Android/java/jni/ZT_jniutils.cpp
Normal file
@@ -0,0 +1,931 @@
|
||||
#include "ZT_jniutils.h"
|
||||
#include "ZT_jnilookup.h"
|
||||
#include <string>
|
||||
#include <assert.h>
|
||||
|
||||
extern JniLookup lookup;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
jobject createResultObject(JNIEnv *env, ZT_ResultCode code)
|
||||
{
|
||||
jclass resultClass = NULL;
|
||||
|
||||
jobject resultObject = NULL;
|
||||
|
||||
resultClass = lookup.findClass("com/zerotier/sdk/ResultCode");
|
||||
if(resultClass == NULL)
|
||||
{
|
||||
LOGE("Couldnt find ResultCode class");
|
||||
return NULL; // exception thrown
|
||||
}
|
||||
|
||||
std::string fieldName;
|
||||
switch(code)
|
||||
{
|
||||
case ZT_RESULT_OK:
|
||||
LOGV("ZT_RESULT_OK");
|
||||
fieldName = "RESULT_OK";
|
||||
break;
|
||||
case ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY:
|
||||
LOGV("ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY");
|
||||
fieldName = "RESULT_FATAL_ERROR_OUT_OF_MEMORY";
|
||||
break;
|
||||
case ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED:
|
||||
LOGV("RESULT_FATAL_ERROR_DATA_STORE_FAILED");
|
||||
fieldName = "RESULT_FATAL_ERROR_DATA_STORE_FAILED";
|
||||
break;
|
||||
case ZT_RESULT_ERROR_NETWORK_NOT_FOUND:
|
||||
LOGV("RESULT_FATAL_ERROR_DATA_STORE_FAILED");
|
||||
fieldName = "RESULT_ERROR_NETWORK_NOT_FOUND";
|
||||
break;
|
||||
case ZT_RESULT_FATAL_ERROR_INTERNAL:
|
||||
default:
|
||||
LOGV("RESULT_FATAL_ERROR_DATA_STORE_FAILED");
|
||||
fieldName = "RESULT_FATAL_ERROR_INTERNAL";
|
||||
break;
|
||||
}
|
||||
|
||||
jfieldID enumField = lookup.findStaticField(resultClass, fieldName.c_str(), "Lcom/zerotier/sdk/ResultCode;");
|
||||
if(env->ExceptionCheck() || enumField == NULL)
|
||||
{
|
||||
LOGE("Error on FindStaticField");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
resultObject = env->GetStaticObjectField(resultClass, enumField);
|
||||
if(env->ExceptionCheck() || resultObject == NULL)
|
||||
{
|
||||
LOGE("Error on GetStaticObjectField");
|
||||
}
|
||||
return resultObject;
|
||||
}
|
||||
|
||||
|
||||
jobject createVirtualNetworkStatus(JNIEnv *env, ZT_VirtualNetworkStatus status)
|
||||
{
|
||||
jobject statusObject = NULL;
|
||||
|
||||
jclass statusClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkStatus");
|
||||
if(statusClass == NULL)
|
||||
{
|
||||
return NULL; // exception thrown
|
||||
}
|
||||
|
||||
std::string fieldName;
|
||||
switch(status)
|
||||
{
|
||||
case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION:
|
||||
fieldName = "NETWORK_STATUS_REQUESTING_CONFIGURATION";
|
||||
break;
|
||||
case ZT_NETWORK_STATUS_OK:
|
||||
fieldName = "NETWORK_STATUS_OK";
|
||||
break;
|
||||
case ZT_NETWORK_STATUS_ACCESS_DENIED:
|
||||
fieldName = "NETWORK_STATUS_ACCESS_DENIED";
|
||||
break;
|
||||
case ZT_NETWORK_STATUS_NOT_FOUND:
|
||||
fieldName = "NETWORK_STATUS_NOT_FOUND";
|
||||
break;
|
||||
case ZT_NETWORK_STATUS_PORT_ERROR:
|
||||
fieldName = "NETWORK_STATUS_PORT_ERROR";
|
||||
break;
|
||||
case ZT_NETWORK_STATUS_CLIENT_TOO_OLD:
|
||||
fieldName = "NETWORK_STATUS_CLIENT_TOO_OLD";
|
||||
break;
|
||||
}
|
||||
|
||||
jfieldID enumField = lookup.findStaticField(statusClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkStatus;");
|
||||
|
||||
statusObject = env->GetStaticObjectField(statusClass, enumField);
|
||||
|
||||
return statusObject;
|
||||
}
|
||||
|
||||
jobject createEvent(JNIEnv *env, ZT_Event event)
|
||||
{
|
||||
jclass eventClass = NULL;
|
||||
jobject eventObject = NULL;
|
||||
|
||||
eventClass = lookup.findClass("com/zerotier/sdk/Event");
|
||||
if(eventClass == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string fieldName;
|
||||
switch(event)
|
||||
{
|
||||
case ZT_EVENT_UP:
|
||||
fieldName = "EVENT_UP";
|
||||
break;
|
||||
case ZT_EVENT_OFFLINE:
|
||||
fieldName = "EVENT_OFFLINE";
|
||||
break;
|
||||
case ZT_EVENT_ONLINE:
|
||||
fieldName = "EVENT_ONLINE";
|
||||
break;
|
||||
case ZT_EVENT_DOWN:
|
||||
fieldName = "EVENT_DOWN";
|
||||
break;
|
||||
case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION:
|
||||
fieldName = "EVENT_FATAL_ERROR_IDENTITY_COLLISION";
|
||||
break;
|
||||
case ZT_EVENT_TRACE:
|
||||
fieldName = "EVENT_TRACE";
|
||||
break;
|
||||
}
|
||||
|
||||
jfieldID enumField = lookup.findStaticField(eventClass, fieldName.c_str(), "Lcom/zerotier/sdk/Event;");
|
||||
|
||||
eventObject = env->GetStaticObjectField(eventClass, enumField);
|
||||
|
||||
return eventObject;
|
||||
}
|
||||
|
||||
jobject createPeerRole(JNIEnv *env, ZT_PeerRole role)
|
||||
{
|
||||
jclass peerRoleClass = NULL;
|
||||
jobject peerRoleObject = NULL;
|
||||
|
||||
peerRoleClass = lookup.findClass("com/zerotier/sdk/PeerRole");
|
||||
if(peerRoleClass == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string fieldName;
|
||||
switch(role)
|
||||
{
|
||||
case ZT_PEER_ROLE_LEAF:
|
||||
fieldName = "PEER_ROLE_LEAF";
|
||||
break;
|
||||
case ZT_PEER_ROLE_RELAY:
|
||||
fieldName = "PEER_ROLE_RELAY";
|
||||
break;
|
||||
case ZT_PEER_ROLE_ROOT:
|
||||
fieldName = "PEER_ROLE_ROOTS";
|
||||
break;
|
||||
}
|
||||
|
||||
jfieldID enumField = lookup.findStaticField(peerRoleClass, fieldName.c_str(), "Lcom/zerotier/sdk/PeerRole;");
|
||||
|
||||
peerRoleObject = env->GetStaticObjectField(peerRoleClass, enumField);
|
||||
|
||||
return peerRoleObject;
|
||||
}
|
||||
|
||||
jobject createVirtualNetworkType(JNIEnv *env, ZT_VirtualNetworkType type)
|
||||
{
|
||||
jclass vntypeClass = NULL;
|
||||
jobject vntypeObject = NULL;
|
||||
|
||||
vntypeClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkType");
|
||||
if(env->ExceptionCheck() || vntypeClass == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string fieldName;
|
||||
switch(type)
|
||||
{
|
||||
case ZT_NETWORK_TYPE_PRIVATE:
|
||||
fieldName = "NETWORK_TYPE_PRIVATE";
|
||||
break;
|
||||
case ZT_NETWORK_TYPE_PUBLIC:
|
||||
fieldName = "NETWORK_TYPE_PUBLIC";
|
||||
break;
|
||||
}
|
||||
|
||||
jfieldID enumField = lookup.findStaticField(vntypeClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkType;");
|
||||
vntypeObject = env->GetStaticObjectField(vntypeClass, enumField);
|
||||
return vntypeObject;
|
||||
}
|
||||
|
||||
jobject createVirtualNetworkConfigOperation(JNIEnv *env, ZT_VirtualNetworkConfigOperation op)
|
||||
{
|
||||
jclass vnetConfigOpClass = NULL;
|
||||
jobject vnetConfigOpObject = NULL;
|
||||
|
||||
vnetConfigOpClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfigOperation");
|
||||
if(env->ExceptionCheck() || vnetConfigOpClass == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string fieldName;
|
||||
switch(op)
|
||||
{
|
||||
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP:
|
||||
fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_UP";
|
||||
break;
|
||||
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE:
|
||||
fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE";
|
||||
break;
|
||||
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN:
|
||||
fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN";
|
||||
break;
|
||||
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY:
|
||||
fieldName = "VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY";
|
||||
break;
|
||||
}
|
||||
|
||||
jfieldID enumField = lookup.findStaticField(vnetConfigOpClass, fieldName.c_str(), "Lcom/zerotier/sdk/VirtualNetworkConfigOperation;");
|
||||
vnetConfigOpObject = env->GetStaticObjectField(vnetConfigOpClass, enumField);
|
||||
return vnetConfigOpObject;
|
||||
}
|
||||
|
||||
jobject newInetAddress(JNIEnv *env, const sockaddr_storage &addr)
|
||||
{
|
||||
LOGV("newInetAddress");
|
||||
jclass inetAddressClass = NULL;
|
||||
jmethodID inetAddress_getByAddress = NULL;
|
||||
|
||||
inetAddressClass = lookup.findClass("java/net/InetAddress");
|
||||
if(env->ExceptionCheck() || inetAddressClass == NULL)
|
||||
{
|
||||
LOGE("Error finding InetAddress class");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inetAddress_getByAddress = lookup.findStaticMethod(
|
||||
inetAddressClass, "getByAddress", "([B)Ljava/net/InetAddress;");
|
||||
if(env->ExceptionCheck() || inetAddress_getByAddress == NULL)
|
||||
{
|
||||
LOGE("Erorr finding getByAddress() static method");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject inetAddressObj = NULL;
|
||||
switch(addr.ss_family)
|
||||
{
|
||||
case AF_INET6:
|
||||
{
|
||||
sockaddr_in6 *ipv6 = (sockaddr_in6*)&addr;
|
||||
jbyteArray buff = env->NewByteArray(16);
|
||||
if(buff == NULL)
|
||||
{
|
||||
LOGE("Error creating IPV6 byte array");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
env->SetByteArrayRegion(buff, 0, 16, (jbyte*)ipv6->sin6_addr.s6_addr);
|
||||
inetAddressObj = env->CallStaticObjectMethod(
|
||||
inetAddressClass, inetAddress_getByAddress, buff);
|
||||
}
|
||||
break;
|
||||
case AF_INET:
|
||||
{
|
||||
sockaddr_in *ipv4 = (sockaddr_in*)&addr;
|
||||
jbyteArray buff = env->NewByteArray(4);
|
||||
if(buff == NULL)
|
||||
{
|
||||
LOGE("Error creating IPV4 byte array");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
env->SetByteArrayRegion(buff, 0, 4, (jbyte*)&ipv4->sin_addr);
|
||||
inetAddressObj = env->CallStaticObjectMethod(
|
||||
inetAddressClass, inetAddress_getByAddress, buff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(env->ExceptionCheck() || inetAddressObj == NULL) {
|
||||
LOGE("Error creating InetAddress object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return inetAddressObj;
|
||||
}
|
||||
|
||||
jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr)
|
||||
{
|
||||
LOGV("newInetSocketAddress Called");
|
||||
jclass inetSocketAddressClass = NULL;
|
||||
jmethodID inetSocketAddress_constructor = NULL;
|
||||
|
||||
inetSocketAddressClass = lookup.findClass("java/net/InetSocketAddress");
|
||||
if(env->ExceptionCheck() || inetSocketAddressClass == NULL)
|
||||
{
|
||||
LOGE("Error finding InetSocketAddress Class");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject inetAddressObject = newInetAddress(env, addr);
|
||||
|
||||
if(env->ExceptionCheck() || inetAddressObject == NULL)
|
||||
{
|
||||
LOGE("Error creating new inet address");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inetSocketAddress_constructor = lookup.findMethod(
|
||||
inetSocketAddressClass, "<init>", "(Ljava/net/InetAddress;I)V");
|
||||
if(env->ExceptionCheck() || inetSocketAddress_constructor == NULL)
|
||||
{
|
||||
LOGE("Error finding InetSocketAddress constructor");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
switch(addr.ss_family)
|
||||
{
|
||||
case AF_INET6:
|
||||
{
|
||||
LOGV("IPV6 Address");
|
||||
sockaddr_in6 *ipv6 = (sockaddr_in6*)&addr;
|
||||
port = ntohs(ipv6->sin6_port);
|
||||
LOGV("Port %d", port);
|
||||
}
|
||||
break;
|
||||
case AF_INET:
|
||||
{
|
||||
LOGV("IPV4 Address");
|
||||
sockaddr_in *ipv4 = (sockaddr_in*)&addr;
|
||||
port = ntohs(ipv4->sin_port);
|
||||
LOGV("Port: %d", port);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
LOGE("ERROR: addr.ss_family is not set or unknown");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
jobject inetSocketAddressObject = env->NewObject(inetSocketAddressClass, inetSocketAddress_constructor, inetAddressObject, port);
|
||||
if(env->ExceptionCheck() || inetSocketAddressObject == NULL) {
|
||||
LOGE("Error creating InetSocketAddress object");
|
||||
}
|
||||
return inetSocketAddressObject;
|
||||
}
|
||||
|
||||
jobject newMulticastGroup(JNIEnv *env, const ZT_MulticastGroup &mc)
|
||||
{
|
||||
jclass multicastGroupClass = NULL;
|
||||
jmethodID multicastGroup_constructor = NULL;
|
||||
|
||||
jfieldID macField = NULL;
|
||||
jfieldID adiField = NULL;
|
||||
|
||||
multicastGroupClass = lookup.findClass("com/zerotier/sdk/MulticastGroup");
|
||||
if(env->ExceptionCheck() || multicastGroupClass == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
multicastGroup_constructor = lookup.findMethod(
|
||||
multicastGroupClass, "<init>", "()V");
|
||||
if(env->ExceptionCheck() || multicastGroup_constructor == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject multicastGroupObj = env->NewObject(multicastGroupClass, multicastGroup_constructor);
|
||||
if(env->ExceptionCheck() || multicastGroupObj == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
macField = lookup.findField(multicastGroupClass, "mac", "J");
|
||||
if(env->ExceptionCheck() || macField == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
adiField = lookup.findField(multicastGroupClass, "adi", "J");
|
||||
if(env->ExceptionCheck() || adiField == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
env->SetLongField(multicastGroupObj, macField, mc.mac);
|
||||
env->SetLongField(multicastGroupObj, adiField, mc.adi);
|
||||
|
||||
return multicastGroupObj;
|
||||
}
|
||||
|
||||
jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp)
|
||||
{
|
||||
LOGV("newPeerPhysicalPath Called");
|
||||
jclass pppClass = NULL;
|
||||
|
||||
jfieldID addressField = NULL;
|
||||
jfieldID lastSendField = NULL;
|
||||
jfieldID lastReceiveField = NULL;
|
||||
jfieldID activeField = NULL;
|
||||
jfieldID preferredField = NULL;
|
||||
|
||||
jmethodID ppp_constructor = NULL;
|
||||
|
||||
pppClass = lookup.findClass("com/zerotier/sdk/PeerPhysicalPath");
|
||||
if(env->ExceptionCheck() || pppClass == NULL)
|
||||
{
|
||||
LOGE("Error finding PeerPhysicalPath class");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
addressField = lookup.findField(pppClass, "address", "Ljava/net/InetSocketAddress;");
|
||||
if(env->ExceptionCheck() || addressField == NULL)
|
||||
{
|
||||
LOGE("Error finding address field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lastSendField = lookup.findField(pppClass, "lastSend", "J");
|
||||
if(env->ExceptionCheck() || lastSendField == NULL)
|
||||
{
|
||||
LOGE("Error finding lastSend field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lastReceiveField = lookup.findField(pppClass, "lastReceive", "J");
|
||||
if(env->ExceptionCheck() || lastReceiveField == NULL)
|
||||
{
|
||||
LOGE("Error finding lastReceive field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
activeField = lookup.findField(pppClass, "active", "Z");
|
||||
if(env->ExceptionCheck() || activeField == NULL)
|
||||
{
|
||||
LOGE("Error finding active field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
preferredField = lookup.findField(pppClass, "preferred", "Z");
|
||||
if(env->ExceptionCheck() || preferredField == NULL)
|
||||
{
|
||||
LOGE("Error finding preferred field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ppp_constructor = lookup.findMethod(pppClass, "<init>", "()V");
|
||||
if(env->ExceptionCheck() || ppp_constructor == NULL)
|
||||
{
|
||||
LOGE("Error finding PeerPhysicalPath constructor");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject pppObject = env->NewObject(pppClass, ppp_constructor);
|
||||
if(env->ExceptionCheck() || pppObject == NULL)
|
||||
{
|
||||
LOGE("Error creating PPP object");
|
||||
return NULL; // out of memory
|
||||
}
|
||||
|
||||
jobject addressObject = newInetSocketAddress(env, ppp.address);
|
||||
if(env->ExceptionCheck() || addressObject == NULL) {
|
||||
LOGE("Error creating InetSocketAddress object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
env->SetObjectField(pppObject, addressField, addressObject);
|
||||
env->SetLongField(pppObject, lastSendField, ppp.lastSend);
|
||||
env->SetLongField(pppObject, lastReceiveField, ppp.lastReceive);
|
||||
env->SetBooleanField(pppObject, activeField, ppp.active);
|
||||
env->SetBooleanField(pppObject, preferredField, ppp.preferred);
|
||||
|
||||
if(env->ExceptionCheck()) {
|
||||
LOGE("Exception assigning fields to PeerPhysicalPath object");
|
||||
}
|
||||
|
||||
return pppObject;
|
||||
}
|
||||
|
||||
jobject newPeer(JNIEnv *env, const ZT_Peer &peer)
|
||||
{
|
||||
LOGV("newPeer called");
|
||||
|
||||
jclass peerClass = NULL;
|
||||
|
||||
jfieldID addressField = NULL;
|
||||
jfieldID lastUnicastFrameField = NULL;
|
||||
jfieldID lastMulticastFrameField = NULL;
|
||||
jfieldID versionMajorField = NULL;
|
||||
jfieldID versionMinorField = NULL;
|
||||
jfieldID versionRevField = NULL;
|
||||
jfieldID latencyField = NULL;
|
||||
jfieldID roleField = NULL;
|
||||
jfieldID pathsField = NULL;
|
||||
|
||||
jmethodID peer_constructor = NULL;
|
||||
|
||||
peerClass = lookup.findClass("com/zerotier/sdk/Peer");
|
||||
if(env->ExceptionCheck() || peerClass == NULL)
|
||||
{
|
||||
LOGE("Error finding Peer class");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
addressField = lookup.findField(peerClass, "address", "J");
|
||||
if(env->ExceptionCheck() || addressField == NULL)
|
||||
{
|
||||
LOGE("Error finding address field of Peer object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lastUnicastFrameField = lookup.findField(peerClass, "lastUnicastFrame", "J");
|
||||
if(env->ExceptionCheck() || lastUnicastFrameField == NULL)
|
||||
{
|
||||
LOGE("Error finding lastUnicastFrame field of Peer object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lastMulticastFrameField = lookup.findField(peerClass, "lastMulticastFrame", "J");
|
||||
if(env->ExceptionCheck() || lastMulticastFrameField == NULL)
|
||||
{
|
||||
LOGE("Error finding lastMulticastFrame field of Peer object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
versionMajorField = lookup.findField(peerClass, "versionMajor", "I");
|
||||
if(env->ExceptionCheck() || versionMajorField == NULL)
|
||||
{
|
||||
LOGE("Error finding versionMajor field of Peer object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
versionMinorField = lookup.findField(peerClass, "versionMinor", "I");
|
||||
if(env->ExceptionCheck() || versionMinorField == NULL)
|
||||
{
|
||||
LOGE("Error finding versionMinor field of Peer object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
versionRevField = lookup.findField(peerClass, "versionRev", "I");
|
||||
if(env->ExceptionCheck() || versionRevField == NULL)
|
||||
{
|
||||
LOGE("Error finding versionRev field of Peer object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
latencyField = lookup.findField(peerClass, "latency", "I");
|
||||
if(env->ExceptionCheck() || latencyField == NULL)
|
||||
{
|
||||
LOGE("Error finding latency field of Peer object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
roleField = lookup.findField(peerClass, "role", "Lcom/zerotier/sdk/PeerRole;");
|
||||
if(env->ExceptionCheck() || roleField == NULL)
|
||||
{
|
||||
LOGE("Error finding role field of Peer object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pathsField = lookup.findField(peerClass, "paths", "[Lcom/zerotier/sdk/PeerPhysicalPath;");
|
||||
if(env->ExceptionCheck() || pathsField == NULL)
|
||||
{
|
||||
LOGE("Error finding paths field of Peer object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
peer_constructor = lookup.findMethod(peerClass, "<init>", "()V");
|
||||
if(env->ExceptionCheck() || peer_constructor == NULL)
|
||||
{
|
||||
LOGE("Error finding Peer constructor");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject peerObject = env->NewObject(peerClass, peer_constructor);
|
||||
if(env->ExceptionCheck() || peerObject == NULL)
|
||||
{
|
||||
LOGE("Error creating Peer object");
|
||||
return NULL; // out of memory
|
||||
}
|
||||
|
||||
env->SetLongField(peerObject, addressField, (jlong)peer.address);
|
||||
env->SetLongField(peerObject, lastUnicastFrameField, (jlong)peer.lastUnicastFrame);
|
||||
env->SetLongField(peerObject, lastMulticastFrameField, (jlong)peer.lastMulticastFrame);
|
||||
env->SetIntField(peerObject, versionMajorField, peer.versionMajor);
|
||||
env->SetIntField(peerObject, versionMinorField, peer.versionMinor);
|
||||
env->SetIntField(peerObject, versionRevField, peer.versionRev);
|
||||
env->SetIntField(peerObject, latencyField, peer.latency);
|
||||
env->SetObjectField(peerObject, roleField, createPeerRole(env, peer.role));
|
||||
|
||||
jclass peerPhysicalPathClass = lookup.findClass("com/zerotier/sdk/PeerPhysicalPath");
|
||||
if(env->ExceptionCheck() || peerPhysicalPathClass == NULL)
|
||||
{
|
||||
LOGE("Error finding PeerPhysicalPath class");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobjectArray arrayObject = env->NewObjectArray(
|
||||
peer.pathCount, peerPhysicalPathClass, NULL);
|
||||
if(env->ExceptionCheck() || arrayObject == NULL)
|
||||
{
|
||||
LOGE("Error creating PeerPhysicalPath[] array");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < peer.pathCount; ++i)
|
||||
{
|
||||
jobject path = newPeerPhysicalPath(env, peer.paths[i]);
|
||||
|
||||
env->SetObjectArrayElement(arrayObject, i, path);
|
||||
if(env->ExceptionCheck()) {
|
||||
LOGE("exception assigning PeerPhysicalPath to array");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
env->SetObjectField(peerObject, pathsField, arrayObject);
|
||||
|
||||
return peerObject;
|
||||
}
|
||||
|
||||
jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &vnetConfig)
|
||||
{
|
||||
jclass vnetConfigClass = NULL;
|
||||
jmethodID vnetConfig_constructor = NULL;
|
||||
jfieldID nwidField = NULL;
|
||||
jfieldID macField = NULL;
|
||||
jfieldID nameField = NULL;
|
||||
jfieldID statusField = NULL;
|
||||
jfieldID typeField = NULL;
|
||||
jfieldID mtuField = NULL;
|
||||
jfieldID dhcpField = NULL;
|
||||
jfieldID bridgeField = NULL;
|
||||
jfieldID broadcastEnabledField = NULL;
|
||||
jfieldID portErrorField = NULL;
|
||||
jfieldID enabledField = NULL;
|
||||
jfieldID netconfRevisionField = NULL;
|
||||
jfieldID multicastSubscriptionsField = NULL;
|
||||
jfieldID assignedAddressesField = NULL;
|
||||
|
||||
vnetConfigClass = lookup.findClass("com/zerotier/sdk/VirtualNetworkConfig");
|
||||
if(vnetConfigClass == NULL)
|
||||
{
|
||||
LOGE("Couldn't find com.zerotier.sdk.VirtualNetworkConfig");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vnetConfig_constructor = lookup.findMethod(
|
||||
vnetConfigClass, "<init>", "()V");
|
||||
if(env->ExceptionCheck() || vnetConfig_constructor == NULL)
|
||||
{
|
||||
LOGE("Couldn't find VirtualNetworkConfig Constructor");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject vnetConfigObj = env->NewObject(vnetConfigClass, vnetConfig_constructor);
|
||||
if(env->ExceptionCheck() || vnetConfigObj == NULL)
|
||||
{
|
||||
LOGE("Error creating new VirtualNetworkConfig object");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nwidField = lookup.findField(vnetConfigClass, "nwid", "J");
|
||||
if(env->ExceptionCheck() || nwidField == NULL)
|
||||
{
|
||||
LOGE("Error getting nwid field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
macField = lookup.findField(vnetConfigClass, "mac", "J");
|
||||
if(env->ExceptionCheck() || macField == NULL)
|
||||
{
|
||||
LOGE("Error getting mac field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nameField = lookup.findField(vnetConfigClass, "name", "Ljava/lang/String;");
|
||||
if(env->ExceptionCheck() || nameField == NULL)
|
||||
{
|
||||
LOGE("Error getting name field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
statusField = lookup.findField(vnetConfigClass, "status", "Lcom/zerotier/sdk/VirtualNetworkStatus;");
|
||||
if(env->ExceptionCheck() || statusField == NULL)
|
||||
{
|
||||
LOGE("Error getting status field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typeField = lookup.findField(vnetConfigClass, "type", "Lcom/zerotier/sdk/VirtualNetworkType;");
|
||||
if(env->ExceptionCheck() || typeField == NULL)
|
||||
{
|
||||
LOGE("Error getting type field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mtuField = lookup.findField(vnetConfigClass, "mtu", "I");
|
||||
if(env->ExceptionCheck() || mtuField == NULL)
|
||||
{
|
||||
LOGE("Error getting mtu field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dhcpField = lookup.findField(vnetConfigClass, "dhcp", "Z");
|
||||
if(env->ExceptionCheck() || dhcpField == NULL)
|
||||
{
|
||||
LOGE("Error getting dhcp field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bridgeField = lookup.findField(vnetConfigClass, "bridge", "Z");
|
||||
if(env->ExceptionCheck() || bridgeField == NULL)
|
||||
{
|
||||
LOGE("Error getting bridge field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
broadcastEnabledField = lookup.findField(vnetConfigClass, "broadcastEnabled", "Z");
|
||||
if(env->ExceptionCheck() || broadcastEnabledField == NULL)
|
||||
{
|
||||
LOGE("Error getting broadcastEnabled field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
portErrorField = lookup.findField(vnetConfigClass, "portError", "I");
|
||||
if(env->ExceptionCheck() || portErrorField == NULL)
|
||||
{
|
||||
LOGE("Error getting portError field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enabledField = lookup.findField(vnetConfigClass, "enabled", "Z");
|
||||
if(env->ExceptionCheck() || enabledField == NULL)
|
||||
{
|
||||
LOGE("Error getting enabled field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
netconfRevisionField = lookup.findField(vnetConfigClass, "netconfRevision", "J");
|
||||
if(env->ExceptionCheck() || netconfRevisionField == NULL)
|
||||
{
|
||||
LOGE("Error getting netconfRevision field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
multicastSubscriptionsField = lookup.findField(vnetConfigClass, "multicastSubscriptions", "[Lcom/zerotier/sdk/MulticastGroup;");
|
||||
if(env->ExceptionCheck() || multicastSubscriptionsField == NULL)
|
||||
{
|
||||
LOGE("Error getting multicastSubscriptions field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assignedAddressesField = lookup.findField(vnetConfigClass, "assignedAddresses", "[Ljava/net/InetSocketAddress;");
|
||||
if(env->ExceptionCheck() || assignedAddressesField == NULL)
|
||||
{
|
||||
LOGE("Error getting assignedAddresses field");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
env->SetLongField(vnetConfigObj, nwidField, vnetConfig.nwid);
|
||||
env->SetLongField(vnetConfigObj, macField, vnetConfig.mac);
|
||||
jstring nameStr = env->NewStringUTF(vnetConfig.name);
|
||||
if(env->ExceptionCheck() || nameStr == NULL)
|
||||
{
|
||||
return NULL; // out of memory
|
||||
}
|
||||
env->SetObjectField(vnetConfigObj, nameField, nameStr);
|
||||
|
||||
jobject statusObject = createVirtualNetworkStatus(env, vnetConfig.status);
|
||||
if(env->ExceptionCheck() || statusObject == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
env->SetObjectField(vnetConfigObj, statusField, statusObject);
|
||||
|
||||
jobject typeObject = createVirtualNetworkType(env, vnetConfig.type);
|
||||
if(env->ExceptionCheck() || typeObject == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
env->SetObjectField(vnetConfigObj, typeField, typeObject);
|
||||
|
||||
env->SetIntField(vnetConfigObj, mtuField, (int)vnetConfig.mtu);
|
||||
env->SetBooleanField(vnetConfigObj, dhcpField, vnetConfig.dhcp);
|
||||
env->SetBooleanField(vnetConfigObj, bridgeField, vnetConfig.bridge);
|
||||
env->SetBooleanField(vnetConfigObj, broadcastEnabledField, vnetConfig.broadcastEnabled);
|
||||
env->SetBooleanField(vnetConfigObj, enabledField, vnetConfig.enabled);
|
||||
env->SetIntField(vnetConfigObj, portErrorField, vnetConfig.portError);
|
||||
|
||||
jclass multicastGroupClass = lookup.findClass("com/zerotier/sdk/MulticastGroup");
|
||||
if(env->ExceptionCheck() || multicastGroupClass == NULL)
|
||||
{
|
||||
LOGE("Error finding MulticastGroup class");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobjectArray mcastSubsArrayObj = env->NewObjectArray(
|
||||
vnetConfig.multicastSubscriptionCount, multicastGroupClass, NULL);
|
||||
if(env->ExceptionCheck() || mcastSubsArrayObj == NULL) {
|
||||
LOGE("Error creating MulticastGroup[] array");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < vnetConfig.multicastSubscriptionCount; ++i)
|
||||
{
|
||||
jobject mcastObj = newMulticastGroup(env, vnetConfig.multicastSubscriptions[i]);
|
||||
env->SetObjectArrayElement(mcastSubsArrayObj, i, mcastObj);
|
||||
if(env->ExceptionCheck())
|
||||
{
|
||||
LOGE("Error assigning MulticastGroup to array");
|
||||
}
|
||||
}
|
||||
env->SetObjectField(vnetConfigObj, multicastSubscriptionsField, mcastSubsArrayObj);
|
||||
|
||||
jclass inetSocketAddressClass = lookup.findClass("java/net/InetSocketAddress");
|
||||
if(env->ExceptionCheck() || inetSocketAddressClass == NULL)
|
||||
{
|
||||
LOGE("Error finding InetSocketAddress class");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobjectArray assignedAddrArrayObj = env->NewObjectArray(
|
||||
vnetConfig.assignedAddressCount, inetSocketAddressClass, NULL);
|
||||
if(env->ExceptionCheck() || assignedAddrArrayObj == NULL)
|
||||
{
|
||||
LOGE("Error creating InetSocketAddress[] array");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < vnetConfig.assignedAddressCount; ++i)
|
||||
{
|
||||
jobject inetAddrObj = newInetSocketAddress(env, vnetConfig.assignedAddresses[i]);
|
||||
env->SetObjectArrayElement(assignedAddrArrayObj, i, inetAddrObj);
|
||||
if(env->ExceptionCheck())
|
||||
{
|
||||
LOGE("Error assigning InetSocketAddress to array");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
env->SetObjectField(vnetConfigObj, assignedAddressesField, assignedAddrArrayObj);
|
||||
|
||||
return vnetConfigObj;
|
||||
}
|
||||
|
||||
jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags)
|
||||
{
|
||||
// create a com.zerotier.sdk.Version object
|
||||
jclass versionClass = NULL;
|
||||
jmethodID versionConstructor = NULL;
|
||||
|
||||
versionClass = lookup.findClass("com/zerotier/sdk/Version");
|
||||
if(env->ExceptionCheck() || versionClass == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
versionConstructor = lookup.findMethod(
|
||||
versionClass, "<init>", "()V");
|
||||
if(env->ExceptionCheck() || versionConstructor == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject versionObj = env->NewObject(versionClass, versionConstructor);
|
||||
if(env->ExceptionCheck() || versionObj == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// copy data to Version object
|
||||
jfieldID majorField = NULL;
|
||||
jfieldID minorField = NULL;
|
||||
jfieldID revisionField = NULL;
|
||||
jfieldID featureFlagsField = NULL;
|
||||
|
||||
majorField = lookup.findField(versionClass, "major", "I");
|
||||
if(env->ExceptionCheck() || majorField == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
minorField = lookup.findField(versionClass, "minor", "I");
|
||||
if(env->ExceptionCheck() || minorField == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
revisionField = lookup.findField(versionClass, "revision", "I");
|
||||
if(env->ExceptionCheck() || revisionField == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
featureFlagsField = lookup.findField(versionClass, "featureFlags", "J");
|
||||
if(env->ExceptionCheck() || featureFlagsField == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
env->SetIntField(versionObj, majorField, (jint)major);
|
||||
env->SetIntField(versionObj, minorField, (jint)minor);
|
||||
env->SetIntField(versionObj, revisionField, (jint)rev);
|
||||
env->SetLongField(versionObj, featureFlagsField, (jlong)featureFlags);
|
||||
|
||||
return versionObj;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
49
integrations/Android/java/jni/ZT_jniutils.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef ZT_jniutils_h_
|
||||
#define ZT_jniutils_h_
|
||||
#include <stdio.h>
|
||||
#include <jni.h>
|
||||
#include <ZeroTierOne.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LOG_TAG "ZeroTierOneJNI"
|
||||
|
||||
#if __ANDROID__
|
||||
#include <android/log.h>
|
||||
#define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#else
|
||||
#define LOGV(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define LOGI(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define LOGD(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define LOGE(...) fprintf(stdout, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
jobject createResultObject(JNIEnv *env, ZT_ResultCode code);
|
||||
jobject createVirtualNetworkStatus(JNIEnv *env, ZT_VirtualNetworkStatus status);
|
||||
jobject createVirtualNetworkType(JNIEnv *env, ZT_VirtualNetworkType type);
|
||||
jobject createEvent(JNIEnv *env, ZT_Event event);
|
||||
jobject createPeerRole(JNIEnv *env, ZT_PeerRole role);
|
||||
jobject createVirtualNetworkConfigOperation(JNIEnv *env, ZT_VirtualNetworkConfigOperation op);
|
||||
|
||||
jobject newInetSocketAddress(JNIEnv *env, const sockaddr_storage &addr);
|
||||
jobject newInetAddress(JNIEnv *env, const sockaddr_storage &addr);
|
||||
|
||||
jobject newMulticastGroup(JNIEnv *env, const ZT_MulticastGroup &mc);
|
||||
|
||||
jobject newPeer(JNIEnv *env, const ZT_Peer &peer);
|
||||
jobject newPeerPhysicalPath(JNIEnv *env, const ZT_PeerPhysicalPath &ppp);
|
||||
|
||||
jobject newNetworkConfig(JNIEnv *env, const ZT_VirtualNetworkConfig &config);
|
||||
|
||||
jobject newVersion(JNIEnv *env, int major, int minor, int rev, long featureFlags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1370
integrations/Android/java/jni/com_zerotierone_sdk_Node.cpp
Normal file
133
integrations/Android/java/jni/com_zerotierone_sdk_Node.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class com_zerotier_sdk_Node */
|
||||
|
||||
#ifndef _Included_com_zerotierone_sdk_Node
|
||||
#define _Included_com_zerotierone_sdk_Node
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: node_init
|
||||
* Signature: (J)Lcom/zerotier/sdk/ResultCode;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_node_1init
|
||||
(JNIEnv *, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: node_delete
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_zerotier_sdk_Node_node_1delete
|
||||
(JNIEnv *, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: processVirtualNetworkFrame
|
||||
* Signature: (JJJJJII[B[J)Lcom/zerotier/sdk/ResultCode;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processVirtualNetworkFrame
|
||||
(JNIEnv *, jobject, jlong, jlong, jlong, jlong, jlong, jint, jint, jbyteArray, jlongArray);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: processWirePacket
|
||||
* Signature: (JJLjava/net/InetSockAddress;Ljava/net/InetSockAddress;[B[J)Lcom/zerotier/sdk/ResultCode;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processWirePacket
|
||||
(JNIEnv *, jobject, jlong, jlong, jobject, jobject, jbyteArray, jlongArray);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: processBackgroundTasks
|
||||
* Signature: (JJ[J)Lcom/zerotier/sdk/ResultCode;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_processBackgroundTasks
|
||||
(JNIEnv *, jobject, jlong, jlong, jlongArray);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: join
|
||||
* Signature: (JJ)Lcom/zerotier/sdk/ResultCode;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_join
|
||||
(JNIEnv *, jobject, jlong, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: leave
|
||||
* Signature: (JJ)Lcom/zerotier/sdk/ResultCode;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_leave
|
||||
(JNIEnv *, jobject, jlong, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: multicastSubscribe
|
||||
* Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastSubscribe
|
||||
(JNIEnv *, jobject, jlong, jlong, jlong, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: multicastUnsubscribe
|
||||
* Signature: (JJJJ)Lcom/zerotier/sdk/ResultCode;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_multicastUnsubscribe
|
||||
(JNIEnv *, jobject, jlong, jlong, jlong, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: address
|
||||
* Signature: (J)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_com_zerotier_sdk_Node_address
|
||||
(JNIEnv *, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: status
|
||||
* Signature: (J)Lcom/zerotier/sdk/NodeStatus;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_status
|
||||
(JNIEnv *, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: networkConfig
|
||||
* Signature: (JJ)Lcom/zerotier/sdk/VirtualNetworkConfig;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_networkConfig
|
||||
(JNIEnv *, jobject, jlong, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: version
|
||||
* Signature: ()Lcom/zerotier/sdk/Version;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_com_zerotier_sdk_Node_version
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: peers
|
||||
* Signature: (J)[Lcom/zerotier/sdk/Peer;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_peers
|
||||
(JNIEnv *, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_zerotier_sdk_Node
|
||||
* Method: networks
|
||||
* Signature: (J)[Lcom/zerotier/sdk/VirtualNetworkConfig;
|
||||
*/
|
||||
JNIEXPORT jobjectArray JNICALL Java_com_zerotier_sdk_Node_networks
|
||||
(JNIEnv *, jobject, jlong);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
package com.zerotier.sdk;
|
||||
|
||||
public interface DataStoreGetListener {
|
||||
|
||||
/**
|
||||
* Function to get an object from the data store
|
||||
*
|
||||
* <p>Object names can contain forward slash (/) path separators. They will
|
||||
* never contain .. or backslash (\), so this is safe to map as a Unix-style
|
||||
* path if the underlying storage permits. For security reasons we recommend
|
||||
* returning errors if .. or \ are used.</p>
|
||||
*
|
||||
* <p>The function must return the actual number of bytes read. If the object
|
||||
* doesn't exist, it should return -1. -2 should be returned on other errors
|
||||
* such as errors accessing underlying storage.</p>
|
||||
*
|
||||
* <p>If the read doesn't fit in the buffer, the max number of bytes should be
|
||||
* read. The caller may call the function multiple times to read the whole
|
||||
* object.</p>
|
||||
*
|
||||
* @param name Name of the object in the data store
|
||||
* @param out_buffer buffer to put the object in
|
||||
* @param bufferIndex index in the object to start reading
|
||||
* @param out_objectSize long[1] to be set to the actual size of the object if it exists.
|
||||
* @return the actual number of bytes read.
|
||||
*/
|
||||
public long onDataStoreGet(
|
||||
String name,
|
||||
byte[] out_buffer,
|
||||
long bufferIndex,
|
||||
long[] out_objectSize);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
package com.zerotier.sdk;
|
||||
|
||||
public interface DataStorePutListener {
|
||||
|
||||
/**
|
||||
* Function to store an object in the data store
|
||||
*
|
||||
* <p>If secure is true, the file should be set readable and writable only
|
||||
* to the user running ZeroTier One. What this means is platform-specific.</p>
|
||||
*
|
||||
* <p>Name semantics are the same as {@link DataStoreGetListener}. This must return
|
||||
* zero on success. You can return any OS-specific error code on failure, as these
|
||||
* may be visible in logs or error messages and might aid in debugging.</p>
|
||||
*
|
||||
* @param name Object name
|
||||
* @param buffer data to store
|
||||
* @param secure set to user read/write only.
|
||||
* @return 0 on success.
|
||||
*/
|
||||
public int onDataStorePut(
|
||||
String name,
|
||||
byte[] buffer,
|
||||
boolean secure);
|
||||
|
||||
/**
|
||||
* Function to delete an object from the data store
|
||||
*
|
||||
* @param name Object name
|
||||
* @return 0 on success.
|
||||
*/
|
||||
public int onDelete(
|
||||
String name);
|
||||
}
|
||||
98
integrations/Android/java/src/com/zerotier/sdk/Event.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
public enum Event {
|
||||
/**
|
||||
* Node has been initialized
|
||||
*
|
||||
* This is the first event generated, and is always sent. It may occur
|
||||
* before Node's constructor returns.
|
||||
*/
|
||||
EVENT_UP,
|
||||
|
||||
/**
|
||||
* Node is offline -- network does not seem to be reachable by any available strategy
|
||||
*/
|
||||
EVENT_OFFLINE,
|
||||
|
||||
/**
|
||||
* Node is online -- at least one upstream node appears reachable
|
||||
*
|
||||
* Meta-data: none
|
||||
*/
|
||||
EVENT_ONLINE,
|
||||
|
||||
/**
|
||||
* Node is shutting down
|
||||
*
|
||||
* <p>This is generated within Node's destructor when it is being shut down.
|
||||
* It's done for convenience, since cleaning up other state in the event
|
||||
* handler may appear more idiomatic.</p>
|
||||
*/
|
||||
EVENT_DOWN,
|
||||
|
||||
/**
|
||||
* Your identity has collided with another node's ZeroTier address
|
||||
*
|
||||
* <p>This happens if two different public keys both hash (via the algorithm
|
||||
* in Identity::generate()) to the same 40-bit ZeroTier address.</p>
|
||||
*
|
||||
* <p>This is something you should "never" see, where "never" is defined as
|
||||
* once per 2^39 new node initializations / identity creations. If you do
|
||||
* see it, you're going to see it very soon after a node is first
|
||||
* initialized.</p>
|
||||
*
|
||||
* <p>This is reported as an event rather than a return code since it's
|
||||
* detected asynchronously via error messages from authoritative nodes.</p>
|
||||
*
|
||||
* <p>If this occurs, you must shut down and delete the node, delete the
|
||||
* identity.secret record/file from the data store, and restart to generate
|
||||
* a new identity. If you don't do this, you will not be able to communicate
|
||||
* with other nodes.</p>
|
||||
*
|
||||
* <p>We'd automate this process, but we don't think silently deleting
|
||||
* private keys or changing our address without telling the calling code
|
||||
* is good form. It violates the principle of least surprise.</p>
|
||||
*
|
||||
* <p>You can technically get away with not handling this, but we recommend
|
||||
* doing so in a mature reliable application. Besides, handling this
|
||||
* condition is a good way to make sure it never arises. It's like how
|
||||
* umbrellas prevent rain and smoke detectors prevent fires. They do, right?</p>
|
||||
*/
|
||||
EVENT_FATAL_ERROR_IDENTITY_COLLISION,
|
||||
|
||||
/**
|
||||
* Trace (debugging) message
|
||||
*
|
||||
* <p>These events are only generated if this is a TRACE-enabled build.</p>
|
||||
*
|
||||
* <p>Meta-data: {@link String}, TRACE message</p>
|
||||
*/
|
||||
EVENT_TRACE
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.lang.String;
|
||||
|
||||
/**
|
||||
* Interface to handle callbacks for ZeroTier One events.
|
||||
*/
|
||||
public interface EventListener {
|
||||
/**
|
||||
* Callback for events with no other associated metadata
|
||||
*
|
||||
* @param event {@link Event} enum
|
||||
*/
|
||||
public void onEvent(Event event);
|
||||
|
||||
/**
|
||||
* Trace messages
|
||||
*
|
||||
* <p>These events are only generated if the underlying ZeroTierOne SDK is a TRACE-enabled build.</p>
|
||||
*
|
||||
* @param message the trace message
|
||||
*/
|
||||
public void onTrace(String message);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
package com.zerotier.sdk;
|
||||
|
||||
|
||||
public final class MulticastGroup {
|
||||
private MulticastGroup() {}
|
||||
|
||||
private long mac;
|
||||
private long adi;
|
||||
|
||||
public boolean equals(MulticastGroup other) {
|
||||
return mac == other.mac && adi == other.adi;
|
||||
}
|
||||
|
||||
/**
|
||||
* MAC address (least significant 48 bits)
|
||||
*/
|
||||
public final long getMacAddress() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional distinguishing information (usually zero)
|
||||
*/
|
||||
public final long getAdi() {
|
||||
return adi;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.zerotier.sdk;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Simple library class for working with JNI (Java Native Interface)
|
||||
*
|
||||
* @see http://adamheinrich.com/2012/how-to-load-native-jni-library-from-jar
|
||||
*
|
||||
* @author Adam Heirnich <adam@adamh.cz>, http://www.adamh.cz
|
||||
*/
|
||||
public class NativeUtils {
|
||||
|
||||
/**
|
||||
* Private constructor - this class will never be instanced
|
||||
*/
|
||||
private NativeUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads library from current JAR archive
|
||||
*
|
||||
* The file from JAR is copied into system temporary directory and then loaded. The temporary file is deleted after exiting.
|
||||
* Method uses String as filename because the pathname is "abstract", not system-dependent.
|
||||
*
|
||||
* @param filename The filename inside JAR as absolute path (beginning with '/'), e.g. /package/File.ext
|
||||
* @throws IOException If temporary file creation or read/write operation fails
|
||||
* @throws IllegalArgumentException If source file (param path) does not exist
|
||||
* @throws IllegalArgumentException If the path is not absolute or if the filename is shorter than three characters (restriction of {@see File#createTempFile(java.lang.String, java.lang.String)}).
|
||||
*/
|
||||
public static void loadLibraryFromJar(String path) throws IOException {
|
||||
|
||||
if (!path.startsWith("/")) {
|
||||
throw new IllegalArgumentException("The path has to be absolute (start with '/').");
|
||||
}
|
||||
|
||||
// Obtain filename from path
|
||||
String[] parts = path.split("/");
|
||||
String filename = (parts.length > 1) ? parts[parts.length - 1] : null;
|
||||
|
||||
// Split filename to prexif and suffix (extension)
|
||||
String prefix = "";
|
||||
String suffix = null;
|
||||
if (filename != null) {
|
||||
parts = filename.split("\\.", 2);
|
||||
prefix = parts[0];
|
||||
suffix = (parts.length > 1) ? "."+parts[parts.length - 1] : null; // Thanks, davs! :-)
|
||||
}
|
||||
|
||||
// Check if the filename is okay
|
||||
if (filename == null || prefix.length() < 3) {
|
||||
throw new IllegalArgumentException("The filename has to be at least 3 characters long.");
|
||||
}
|
||||
|
||||
// Prepare temporary file
|
||||
File temp = File.createTempFile(prefix, suffix);
|
||||
temp.deleteOnExit();
|
||||
|
||||
if (!temp.exists()) {
|
||||
throw new FileNotFoundException("File " + temp.getAbsolutePath() + " does not exist.");
|
||||
}
|
||||
|
||||
// Prepare buffer for data copying
|
||||
byte[] buffer = new byte[1024];
|
||||
int readBytes;
|
||||
|
||||
// Open and check input stream
|
||||
InputStream is = NativeUtils.class.getResourceAsStream(path);
|
||||
if (is == null) {
|
||||
throw new FileNotFoundException("File " + path + " was not found inside JAR.");
|
||||
}
|
||||
|
||||
// Open output stream and copy data between source file in JAR and the temporary file
|
||||
OutputStream os = new FileOutputStream(temp);
|
||||
try {
|
||||
while ((readBytes = is.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, readBytes);
|
||||
}
|
||||
} finally {
|
||||
// If read/write fails, close streams safely before throwing an exception
|
||||
os.close();
|
||||
is.close();
|
||||
}
|
||||
|
||||
// Finally, load the library
|
||||
System.load(temp.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
442
integrations/Android/java/src/com/zerotier/sdk/Node.java
Normal file
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A ZeroTier One node
|
||||
*/
|
||||
public class Node {
|
||||
|
||||
//public static native int load_symbols();
|
||||
|
||||
static {
|
||||
try {
|
||||
//System.loadLibrary("ZeroTierOneJNI");
|
||||
|
||||
// test call
|
||||
//System.out.println("load_symbols() = " + load_symbols());
|
||||
|
||||
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
try {
|
||||
if(System.getProperty("os.name").startsWith("Windows")) {
|
||||
System.out.println("Arch: " + System.getProperty("sun.arch.data.model"));
|
||||
if(System.getProperty("sun.arch.data.model").equals("64")) {
|
||||
NativeUtils.loadLibraryFromJar("/lib/ZeroTierOneJNI_win64.dll");
|
||||
} else {
|
||||
NativeUtils.loadLibraryFromJar("/lib/ZeroTierOneJNI_win32.dll");
|
||||
}
|
||||
} else if(System.getProperty("os.name").startsWith("Mac")) {
|
||||
NativeUtils.loadLibraryFromJar("/lib/libZeroTierOneJNI.jnilib");
|
||||
} else {
|
||||
// TODO: Linux
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final String TAG = "NODE";
|
||||
|
||||
/**
|
||||
* Node ID for JNI purposes.
|
||||
* Currently set to the now value passed in at the constructor
|
||||
*
|
||||
* -1 if the node has already been closed
|
||||
*/
|
||||
private long nodeId;
|
||||
|
||||
private final DataStoreGetListener getListener;
|
||||
private final DataStorePutListener putListener;
|
||||
private final PacketSender sender;
|
||||
private final EventListener eventListener;
|
||||
private final VirtualNetworkFrameListener frameListener;
|
||||
private final VirtualNetworkConfigListener configListener;
|
||||
|
||||
/**
|
||||
* Create a new ZeroTier One node
|
||||
*
|
||||
* <p>Note that this can take a few seconds the first time it's called, as it
|
||||
* will generate an identity.</p>
|
||||
*
|
||||
* @param now Current clock in milliseconds
|
||||
* @param getListener User written instance of the {@link DataStoreGetListener} interface called to get objects from persistent storage. This instance must be unique per Node object.
|
||||
* @param putListener User written intstance of the {@link DataStorePutListener} interface called to put objects in persistent storage. This instance must be unique per Node object.
|
||||
* @param sender
|
||||
* @param eventListener User written instance of the {@link EventListener} interface to receive status updates and non-fatal error notices. This instance must be unique per Node object.
|
||||
* @param frameListener
|
||||
* @param configListener User written instance of the {@link VirtualNetworkConfigListener} interface to be called when virtual LANs are created, deleted, or their config parameters change. This instance must be unique per Node object.
|
||||
*/
|
||||
public Node(long now,
|
||||
DataStoreGetListener getListener,
|
||||
DataStorePutListener putListener,
|
||||
PacketSender sender,
|
||||
EventListener eventListener,
|
||||
VirtualNetworkFrameListener frameListener,
|
||||
VirtualNetworkConfigListener configListener) throws NodeException
|
||||
{
|
||||
this.nodeId = now;
|
||||
|
||||
this.getListener = getListener;
|
||||
this.putListener = putListener;
|
||||
this.sender = sender;
|
||||
this.eventListener = eventListener;
|
||||
this.frameListener = frameListener;
|
||||
this.configListener = configListener;
|
||||
|
||||
ResultCode rc = node_init(now);
|
||||
if(rc != ResultCode.RESULT_OK)
|
||||
{
|
||||
// TODO: Throw Exception
|
||||
throw new NodeException(rc.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this Node.
|
||||
*
|
||||
* <p>The Node object can no longer be used once this method is called.</p>
|
||||
*/
|
||||
public void close() {
|
||||
if(nodeId != -1) {
|
||||
node_delete(nodeId);
|
||||
nodeId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a frame from a virtual network port
|
||||
*
|
||||
* @param now Current clock in milliseconds
|
||||
* @param nwid ZeroTier 64-bit virtual network ID
|
||||
* @param sourceMac Source MAC address (least significant 48 bits)
|
||||
* @param destMac Destination MAC address (least significant 48 bits)
|
||||
* @param etherType 16-bit Ethernet frame type
|
||||
* @param vlanId 10-bit VLAN ID or 0 if none
|
||||
* @param frameData Frame payload data
|
||||
* @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
|
||||
* @return OK (0) or error code if a fatal error condition has occurred
|
||||
*/
|
||||
public ResultCode processVirtualNetworkFrame(
|
||||
long now,
|
||||
long nwid,
|
||||
long sourceMac,
|
||||
long destMac,
|
||||
int etherType,
|
||||
int vlanId,
|
||||
byte[] frameData,
|
||||
long[] nextBackgroundTaskDeadline) {
|
||||
return processVirtualNetworkFrame(
|
||||
nodeId, now, nwid, sourceMac, destMac, etherType, vlanId,
|
||||
frameData, nextBackgroundTaskDeadline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a packet received from the physical wire
|
||||
*
|
||||
* @param now Current clock in milliseconds
|
||||
* @param remoteAddress Origin of packet
|
||||
* @param packetData Packet data
|
||||
* @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
|
||||
* @return OK (0) or error code if a fatal error condition has occurred
|
||||
*/
|
||||
public ResultCode processWirePacket(
|
||||
long now,
|
||||
InetSocketAddress localAddress,
|
||||
InetSocketAddress remoteAddress,
|
||||
byte[] packetData,
|
||||
long[] nextBackgroundTaskDeadline) {
|
||||
return processWirePacket(
|
||||
nodeId, now, localAddress, remoteAddress, packetData,
|
||||
nextBackgroundTaskDeadline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform periodic background operations
|
||||
*
|
||||
* @param now Current clock in milliseconds
|
||||
* @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
|
||||
* @return OK (0) or error code if a fatal error condition has occurred
|
||||
*/
|
||||
public ResultCode processBackgroundTasks(long now, long[] nextBackgroundTaskDeadline) {
|
||||
return processBackgroundTasks(nodeId, now, nextBackgroundTaskDeadline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join a network
|
||||
*
|
||||
* <p>This may generate calls to the port config callback before it returns,
|
||||
* or these may be deffered if a netconf is not available yet.</p>
|
||||
*
|
||||
* <p>If we are already a member of the network, nothing is done and OK is
|
||||
* returned.</p>
|
||||
*
|
||||
* @param nwid 64-bit ZeroTier network ID
|
||||
* @return OK (0) or error code if a fatal error condition has occurred
|
||||
*/
|
||||
public ResultCode join(long nwid) {
|
||||
return join(nodeId, nwid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave a network
|
||||
*
|
||||
* <p>If a port has been configured for this network this will generate a call
|
||||
* to the port config callback with a NULL second parameter to indicate that
|
||||
* the port is now deleted.</p>
|
||||
*
|
||||
* @param nwid 64-bit network ID
|
||||
* @return OK (0) or error code if a fatal error condition has occurred
|
||||
*/
|
||||
public ResultCode leave(long nwid) {
|
||||
return leave(nodeId, nwid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to an Ethernet multicast group
|
||||
*
|
||||
* <p>For IPv4 ARP, the implementation must subscribe to 0xffffffffffff (the
|
||||
* broadcast address) but with an ADI equal to each IPv4 address in host
|
||||
* byte order. This converts ARP from a non-scalable broadcast protocol to
|
||||
* a scalable multicast protocol with perfect address specificity.</p>
|
||||
*
|
||||
* <p>If this is not done, ARP will not work reliably.</p>
|
||||
*
|
||||
* <p>Multiple calls to subscribe to the same multicast address will have no
|
||||
* effect. It is perfectly safe to do this.</p>
|
||||
*
|
||||
* <p>This does not generate an update call to the {@link VirtualNetworkConfigListener#onNetworkConfigurationUpdated} method.</p>
|
||||
*
|
||||
* @param nwid 64-bit network ID
|
||||
* @param multicastGroup Ethernet multicast or broadcast MAC (least significant 48 bits)
|
||||
* @return OK (0) or error code if a fatal error condition has occurred
|
||||
*/
|
||||
public ResultCode multicastSubscribe(
|
||||
long nwid,
|
||||
long multicastGroup) {
|
||||
return multicastSubscribe(nodeId, nwid, multicastGroup, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to an Ethernet multicast group
|
||||
*
|
||||
* <p>ADI stands for additional distinguishing information. This defaults to zero
|
||||
* and is rarely used. Right now its only use is to enable IPv4 ARP to scale,
|
||||
* and this must be done.</p>
|
||||
*
|
||||
* <p>For IPv4 ARP, the implementation must subscribe to 0xffffffffffff (the
|
||||
* broadcast address) but with an ADI equal to each IPv4 address in host
|
||||
* byte order. This converts ARP from a non-scalable broadcast protocol to
|
||||
* a scalable multicast protocol with perfect address specificity.</p>
|
||||
*
|
||||
* <p>If this is not done, ARP will not work reliably.</p>
|
||||
*
|
||||
* <p>Multiple calls to subscribe to the same multicast address will have no
|
||||
* effect. It is perfectly safe to do this.</p>
|
||||
*
|
||||
* <p>This does not generate an update call to the {@link VirtualNetworkConfigListener#onNetworkConfigurationUpdated} method.</p>
|
||||
*
|
||||
* @param nwid 64-bit network ID
|
||||
* @param multicastGroup Ethernet multicast or broadcast MAC (least significant 48 bits)
|
||||
* @param multicastAdi Multicast ADI (least significant 32 bits only, default: 0)
|
||||
* @return OK (0) or error code if a fatal error condition has occurred
|
||||
*/
|
||||
public ResultCode multicastSubscribe(
|
||||
long nwid,
|
||||
long multicastGroup,
|
||||
long multicastAdi) {
|
||||
return multicastSubscribe(nodeId, nwid, multicastGroup, multicastAdi);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unsubscribe from an Ethernet multicast group (or all groups)
|
||||
*
|
||||
* <p>If multicastGroup is zero (0), this will unsubscribe from all groups. If
|
||||
* you are not subscribed to a group this has no effect.</p>
|
||||
*
|
||||
* <p>This does not generate an update call to the {@link VirtualNetworkConfigListener#onNetworkConfigurationUpdated} method.</p>
|
||||
*
|
||||
* @param nwid 64-bit network ID
|
||||
* @param multicastGroup Ethernet multicast or broadcast MAC (least significant 48 bits)
|
||||
* @return OK (0) or error code if a fatal error condition has occurred
|
||||
*/
|
||||
public ResultCode multicastUnsubscribe(
|
||||
long nwid,
|
||||
long multicastGroup) {
|
||||
return multicastUnsubscribe(nodeId, nwid, multicastGroup, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from an Ethernet multicast group (or all groups)
|
||||
*
|
||||
* <p>If multicastGroup is zero (0), this will unsubscribe from all groups. If
|
||||
* you are not subscribed to a group this has no effect.</p>
|
||||
*
|
||||
* <p>This does not generate an update call to the {@link VirtualNetworkConfigListener#onNetworkConfigurationUpdated} method.</p>
|
||||
*
|
||||
* <p>ADI stands for additional distinguishing information. This defaults to zero
|
||||
* and is rarely used. Right now its only use is to enable IPv4 ARP to scale,
|
||||
* and this must be done.</p>
|
||||
*
|
||||
* @param nwid 64-bit network ID
|
||||
* @param multicastGroup Ethernet multicast or broadcast MAC (least significant 48 bits)
|
||||
* @param multicastAdi Multicast ADI (least significant 32 bits only, default: 0)
|
||||
* @return OK (0) or error code if a fatal error condition has occurred
|
||||
*/
|
||||
public ResultCode multicastUnsubscribe(
|
||||
long nwid,
|
||||
long multicastGroup,
|
||||
long multicastAdi) {
|
||||
return multicastUnsubscribe(nodeId, nwid, multicastGroup, multicastAdi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this node's 40-bit ZeroTier address
|
||||
*
|
||||
* @return ZeroTier address (least significant 40 bits of 64-bit int)
|
||||
*/
|
||||
public long address() {
|
||||
return address(nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of this node
|
||||
*
|
||||
* @return @{link NodeStatus} struct with the current node status.
|
||||
*/
|
||||
public NodeStatus status() {
|
||||
return status(nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of known peer nodes
|
||||
*
|
||||
* @return List of known peers or NULL on failure
|
||||
*/
|
||||
public Peer[] peers() {
|
||||
return peers(nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of a virtual network
|
||||
*
|
||||
* @param nwid 64-bit network ID
|
||||
* @return {@link VirtualNetworkConfig} or NULL if we are not a member of this network
|
||||
*/
|
||||
public VirtualNetworkConfig networkConfig(long nwid) {
|
||||
return networkConfig(nodeId, nwid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate and get status of all networks
|
||||
*
|
||||
* @return List of networks or NULL on failure
|
||||
*/
|
||||
public VirtualNetworkConfig[] networks() {
|
||||
return networks(nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ZeroTier One version
|
||||
*
|
||||
* @return {@link Version} object with ZeroTierOne version information.
|
||||
*/
|
||||
public Version getVersion() {
|
||||
return version();
|
||||
}
|
||||
|
||||
//
|
||||
// function declarations for JNI
|
||||
//
|
||||
private native ResultCode node_init(long now);
|
||||
|
||||
private native void node_delete(long nodeId);
|
||||
|
||||
private native ResultCode processVirtualNetworkFrame(
|
||||
long nodeId,
|
||||
long now,
|
||||
long nwid,
|
||||
long sourceMac,
|
||||
long destMac,
|
||||
int etherType,
|
||||
int vlanId,
|
||||
byte[] frameData,
|
||||
long[] nextBackgroundTaskDeadline);
|
||||
|
||||
private native ResultCode processWirePacket(
|
||||
long nodeId,
|
||||
long now,
|
||||
InetSocketAddress localAddress,
|
||||
InetSocketAddress remoteAddress,
|
||||
byte[] packetData,
|
||||
long[] nextBackgroundTaskDeadline);
|
||||
|
||||
private native ResultCode processBackgroundTasks(
|
||||
long nodeId,
|
||||
long now,
|
||||
long[] nextBackgroundTaskDeadline);
|
||||
|
||||
private native ResultCode join(long nodeId, long nwid);
|
||||
|
||||
private native ResultCode leave(long nodeId, long nwid);
|
||||
|
||||
private native ResultCode multicastSubscribe(
|
||||
long nodeId,
|
||||
long nwid,
|
||||
long multicastGroup,
|
||||
long multicastAdi);
|
||||
|
||||
private native ResultCode multicastUnsubscribe(
|
||||
long nodeId,
|
||||
long nwid,
|
||||
long multicastGroup,
|
||||
long multicastAdi);
|
||||
|
||||
private native long address(long nodeId);
|
||||
|
||||
private native NodeStatus status(long nodeId);
|
||||
|
||||
private native VirtualNetworkConfig networkConfig(long nodeId, long nwid);
|
||||
|
||||
private native Version version();
|
||||
|
||||
private native Peer[] peers(long nodeId);
|
||||
|
||||
private native VirtualNetworkConfig[] networks(long nodeId);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
import java.lang.RuntimeException;
|
||||
|
||||
public class NodeException extends RuntimeException {
|
||||
public NodeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
public final class NodeStatus {
|
||||
private long address;
|
||||
private String publicIdentity;
|
||||
private String secretIdentity;
|
||||
private boolean online;
|
||||
|
||||
private NodeStatus() {}
|
||||
|
||||
/**
|
||||
* 40-bit ZeroTier address of this node
|
||||
*/
|
||||
public final long getAddres() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public identity in string-serialized form (safe to send to others)
|
||||
*
|
||||
* <p>This identity will remain valid as long as the node exists.</p>
|
||||
*/
|
||||
public final String getPublicIdentity() {
|
||||
return publicIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full identity including secret key in string-serialized form
|
||||
*
|
||||
* <p>This identity will remain valid as long as the node exists.</p>
|
||||
*/
|
||||
public final String getSecretIdentity() {
|
||||
return secretIdentity;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if some kind of connectivity appears available
|
||||
*/
|
||||
public final boolean isOnline() {
|
||||
return online;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
package com.zerotier.sdk;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
|
||||
public interface PacketSender {
|
||||
/**
|
||||
* Function to send a ZeroTier packet out over the wire
|
||||
*
|
||||
* <p>The function must return zero on success and may return any error code
|
||||
* on failure. Note that success does not (of course) guarantee packet
|
||||
* delivery. It only means that the packet appears to have been sent.</p>
|
||||
*
|
||||
* @param 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 localAddr,
|
||||
InetSocketAddress remoteAddr,
|
||||
byte[] packetData,
|
||||
int ttl);
|
||||
}
|
||||
110
integrations/Android/java/src/com/zerotier/sdk/Peer.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Peer status result
|
||||
*/
|
||||
public final class Peer {
|
||||
private long address;
|
||||
private long lastUnicastFrame;
|
||||
private long lastMulticastFrame;
|
||||
private int versionMajor;
|
||||
private int versionMinor;
|
||||
private int versionRev;
|
||||
private int latency;
|
||||
private PeerRole role;
|
||||
private PeerPhysicalPath[] paths;
|
||||
|
||||
private Peer() {}
|
||||
|
||||
/**
|
||||
* ZeroTier address (40 bits)
|
||||
*/
|
||||
public final long address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time we last received a unicast frame from this peer
|
||||
*/
|
||||
public final long lastUnicastFrame() {
|
||||
return lastUnicastFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time we last received a multicast rame from this peer
|
||||
*/
|
||||
public final long lastMulticastFrame() {
|
||||
return lastMulticastFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote major version or -1 if not known
|
||||
*/
|
||||
public final int versionMajor() {
|
||||
return versionMajor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote minor version or -1 if not known
|
||||
*/
|
||||
public final int versionMinor() {
|
||||
return versionMinor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote revision or -1 if not known
|
||||
*/
|
||||
public final int versionRev() {
|
||||
return versionRev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Last measured latency in milliseconds or zero if unknown
|
||||
*/
|
||||
public final int latency() {
|
||||
return latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* What trust hierarchy role does this device have?
|
||||
*/
|
||||
public final PeerRole role() {
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Known network paths to peer
|
||||
*/
|
||||
public final PeerPhysicalPath[] paths() {
|
||||
return paths;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* Physical network path to a peer
|
||||
*/
|
||||
public final class PeerPhysicalPath {
|
||||
private InetSocketAddress address;
|
||||
private long lastSend;
|
||||
private long lastReceive;
|
||||
private boolean fixed;
|
||||
private boolean active;
|
||||
private boolean preferred;
|
||||
|
||||
private PeerPhysicalPath() {}
|
||||
|
||||
/**
|
||||
* Address of endpoint
|
||||
*/
|
||||
public final InetSocketAddress address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time of last send in milliseconds or 0 for never
|
||||
*/
|
||||
public final long lastSend() {
|
||||
return lastSend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time of last receive in milliseconds or 0 for never
|
||||
*/
|
||||
public final long lastReceive() {
|
||||
return lastReceive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is path fixed? (i.e. not learned, static)
|
||||
*/
|
||||
public final boolean isFixed() {
|
||||
return fixed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is path active?
|
||||
*/
|
||||
public final boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is path preferred?
|
||||
*/
|
||||
public final boolean isPreferred() {
|
||||
return preferred;
|
||||
}
|
||||
}
|
||||
45
integrations/Android/java/src/com/zerotier/sdk/PeerRole.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
public enum PeerRole {
|
||||
/**
|
||||
* An ordinary node
|
||||
*/
|
||||
PEER_ROLE_LEAF,
|
||||
|
||||
/**
|
||||
* relay node
|
||||
*/
|
||||
PEER_ROLE_RELAY,
|
||||
|
||||
/**
|
||||
* root server
|
||||
*/
|
||||
PEER_ROLE_ROOT
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
/**
|
||||
* Function return code: OK (0) or error results
|
||||
*
|
||||
* <p>Use {@link ResultCode#isFatal) to check for a fatal error. If a fatal error
|
||||
* occurs, the node should be considered to not be working correctly. These
|
||||
* indicate serious problems like an inaccessible data store or a compile
|
||||
* problem.</p>
|
||||
*/
|
||||
public enum ResultCode {
|
||||
/**
|
||||
* Operation completed normally
|
||||
*/
|
||||
RESULT_OK(0),
|
||||
|
||||
// Fatal errors (> 0, < 1000)
|
||||
/**
|
||||
* Ran out of memory
|
||||
*/
|
||||
RESULT_FATAL_ERROR_OUT_OF_MEMORY(1),
|
||||
|
||||
/**
|
||||
* Data store is not writable or has failed
|
||||
*/
|
||||
RESULT_FATAL_ERROR_DATA_STORE_FAILED(2),
|
||||
|
||||
/**
|
||||
* Internal error (e.g. unexpected exception indicating bug or build problem)
|
||||
*/
|
||||
RESULT_FATAL_ERROR_INTERNAL(3),
|
||||
|
||||
// non-fatal errors
|
||||
|
||||
/**
|
||||
* Network ID not valid
|
||||
*/
|
||||
RESULT_ERROR_NETWORK_NOT_FOUND(1000);
|
||||
|
||||
private final int id;
|
||||
ResultCode(int id) { this.id = id; }
|
||||
public int getValue() { return id; }
|
||||
|
||||
public boolean isFatal(int id) {
|
||||
return (id > 0 && id < 1000);
|
||||
}
|
||||
}
|
||||
37
integrations/Android/java/src/com/zerotier/sdk/Version.java
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
public final class Version {
|
||||
private Version() {}
|
||||
|
||||
public int major = 0;
|
||||
public int minor = 0;
|
||||
public int revision = 0;
|
||||
public long featureFlags = 0;
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
import java.lang.Comparable;
|
||||
import java.lang.Override;
|
||||
import java.lang.String;
|
||||
import java.util.ArrayList;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public final class VirtualNetworkConfig implements Comparable<VirtualNetworkConfig> {
|
||||
public static final int MAX_MULTICAST_SUBSCRIPTIONS = 4096;
|
||||
public static final int ZT_MAX_ZT_ASSIGNED_ADDRESSES = 16;
|
||||
|
||||
private long nwid;
|
||||
private long mac;
|
||||
private String name;
|
||||
private VirtualNetworkStatus status;
|
||||
private VirtualNetworkType type;
|
||||
private int mtu;
|
||||
private boolean dhcp;
|
||||
private boolean bridge;
|
||||
private boolean broadcastEnabled;
|
||||
private int portError;
|
||||
private boolean enabled;
|
||||
private long netconfRevision;
|
||||
private MulticastGroup[] multicastSubscriptions;
|
||||
private InetSocketAddress[] assignedAddresses;
|
||||
|
||||
private VirtualNetworkConfig() {
|
||||
|
||||
}
|
||||
|
||||
public boolean equals(VirtualNetworkConfig cfg) {
|
||||
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 &&
|
||||
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
|
||||
*/
|
||||
public final long networkId() {
|
||||
return nwid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ethernet MAC (40 bits) that should be assigned to port
|
||||
*/
|
||||
public final long macAddress() {
|
||||
return mac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network name (from network configuration master)
|
||||
*/
|
||||
public final String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network configuration request status
|
||||
*/
|
||||
public final VirtualNetworkStatus networkStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network type
|
||||
*/
|
||||
public final VirtualNetworkType networkType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum interface MTU
|
||||
*/
|
||||
public final int mtu() {
|
||||
return mtu;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the network this port belongs to indicates DHCP availability
|
||||
*
|
||||
* <p>This is a suggestion. The underlying implementation is free to ignore it
|
||||
* for security or other reasons. This is simply a netconf parameter that
|
||||
* means 'DHCP is available on this network.'</p>
|
||||
*/
|
||||
public final boolean isDhcpAvailable() {
|
||||
return dhcp;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this port is allowed to bridge to other networks
|
||||
*
|
||||
* <p>This is informational. If this is false, bridged packets will simply
|
||||
* be dropped and bridging won't work.</p>
|
||||
*/
|
||||
public final boolean isBridgeEnabled() {
|
||||
return bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, this network supports and allows broadcast (ff:ff:ff:ff:ff:ff) traffic
|
||||
*/
|
||||
public final boolean broadcastEnabled() {
|
||||
return broadcastEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the network is in PORT_ERROR state, this is the error most recently returned by the port config callback
|
||||
*/
|
||||
public final int portError() {
|
||||
return portError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this network enabled? If not, all frames to/from are dropped.
|
||||
*/
|
||||
public final boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network config revision as reported by netconf master
|
||||
*
|
||||
* <p>If this is zero, it means we're still waiting for our netconf.</p>
|
||||
*/
|
||||
public final long netconfRevision() {
|
||||
return netconfRevision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multicast group subscriptions
|
||||
*/
|
||||
public final MulticastGroup[] multicastSubscriptions() {
|
||||
return multicastSubscriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* ZeroTier-assigned addresses (in {@link java.net.InetSocketAddress} objects)
|
||||
*
|
||||
* For IP, the port number of the sockaddr_XX structure contains the number
|
||||
* of bits in the address netmask. Only the IP address and port are used.
|
||||
* Other fields like interface number can be ignored.
|
||||
*
|
||||
* This is only used for ZeroTier-managed address assignments sent by the
|
||||
* virtual network's configuration master.
|
||||
*/
|
||||
public final InetSocketAddress[] assignedAddresses() {
|
||||
return assignedAddresses;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
|
||||
public interface VirtualNetworkConfigListener {
|
||||
/**
|
||||
* Callback called to update virtual network port configuration
|
||||
*
|
||||
* <p>This can be called at any time to update the configuration of a virtual
|
||||
* network port. The parameter after the network ID specifies whether this
|
||||
* port is being brought up, updated, brought down, or permanently deleted.
|
||||
*
|
||||
* This in turn should be used by the underlying implementation to create
|
||||
* and configure tap devices at the OS (or virtual network stack) layer.</P>
|
||||
*
|
||||
* This should not call {@link Node#multicastSubscribe} or other network-modifying
|
||||
* methods, as this could cause a deadlock in multithreaded or interrupt
|
||||
* driven environments.
|
||||
*
|
||||
* This must return 0 on success. It can return any OS-dependent error code
|
||||
* on failure, and this results in the network being placed into the
|
||||
* PORT_ERROR state.
|
||||
*
|
||||
* @param nwid network id
|
||||
* @param op {@link VirtualNetworkConfigOperation} enum describing the configuration operation
|
||||
* @param config {@link VirtualNetworkConfig} object with the new configuration
|
||||
* @return 0 on success
|
||||
*/
|
||||
public int onNetworkConfigurationUpdated(
|
||||
long nwid,
|
||||
VirtualNetworkConfigOperation op,
|
||||
VirtualNetworkConfig config);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
package com.zerotier.sdk;
|
||||
|
||||
public enum VirtualNetworkConfigOperation {
|
||||
/**
|
||||
* Network is coming up (either for the first time or after service restart)
|
||||
*/
|
||||
VIRTUAL_NETWORK_CONFIG_OPERATION_UP,
|
||||
|
||||
/**
|
||||
* Network configuration has been updated
|
||||
*/
|
||||
VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE,
|
||||
|
||||
/**
|
||||
* Network is going down (not permanently)
|
||||
*/
|
||||
VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,
|
||||
|
||||
/**
|
||||
* Network is going down permanently (leave/delete)
|
||||
*/
|
||||
VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
|
||||
package com.zerotier.sdk;
|
||||
|
||||
public interface VirtualNetworkFrameListener {
|
||||
/**
|
||||
* Function to send a frame out to a virtual network port
|
||||
*
|
||||
* @param nwid ZeroTier One network ID
|
||||
* @param srcMac source MAC address
|
||||
* @param destMac destination MAC address
|
||||
* @param ethertype
|
||||
* @param vlanId
|
||||
* @param frameData data to send
|
||||
*/
|
||||
public void onVirtualNetworkFrame(
|
||||
long nwid,
|
||||
long srcMac,
|
||||
long destMac,
|
||||
long etherType,
|
||||
long vlanId,
|
||||
byte[] frameData);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
package com.zerotier.sdk;
|
||||
|
||||
public enum VirtualNetworkStatus {
|
||||
/**
|
||||
* Waiting for network configuration (also means revision == 0)
|
||||
*/
|
||||
NETWORK_STATUS_REQUESTING_CONFIGURATION,
|
||||
|
||||
/**
|
||||
* Configuration received and we are authorized
|
||||
*/
|
||||
NETWORK_STATUS_OK,
|
||||
|
||||
/**
|
||||
* Netconf master told us 'nope'
|
||||
*/
|
||||
NETWORK_STATUS_ACCESS_DENIED,
|
||||
|
||||
/**
|
||||
* Netconf master exists, but this virtual network does not
|
||||
*/
|
||||
NETWORK_STATUS_NOT_FOUND,
|
||||
|
||||
/**
|
||||
* Initialization of network failed or other internal error
|
||||
*/
|
||||
NETWORK_STATUS_PORT_ERROR,
|
||||
|
||||
/**
|
||||
* ZeroTier One version too old
|
||||
*/
|
||||
NETWORK_STATUS_CLIENT_TOO_OLD
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2015 ZeroTier, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* ZeroTier may be used and distributed under the terms of the GPLv3, which
|
||||
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
*
|
||||
* If you would like to embed ZeroTier into a commercial application or
|
||||
* redistribute it in a modified binary form, please contact ZeroTier Networks
|
||||
* LLC. Start here: http://www.zerotier.com/
|
||||
*/
|
||||
package com.zerotier.sdk;
|
||||
|
||||
public enum VirtualNetworkType {
|
||||
/**
|
||||
* Private networks are authorized via certificates of membership
|
||||
*/
|
||||
NETWORK_TYPE_PRIVATE,
|
||||
|
||||
/**
|
||||
* Public networks have no access control -- they'll always be AUTHORIZED
|
||||
*/
|
||||
NETWORK_TYPE_PUBLIC
|
||||
}
|
||||
16
integrations/Android/proj/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
ZeroTier One for Android
|
||||
=====
|
||||
|
||||
Fully integrated build of ZeroTier JNI + the Android application.
|
||||
|
||||
|
||||
To Build
|
||||
------
|
||||
|
||||
1. Open project in Android Studio (Eclipse is not supported)
|
||||
2. Open the file local.properties
|
||||
3. Add the following line:
|
||||
|
||||
`ndk.dir=/path/to/your/local/ndk`
|
||||
|
||||
Once that is done, it should be buildable, runnable, debuggable, etc.
|
||||
108
integrations/Android/proj/app/app.iml
Normal file
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="proj" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="android-gradle" name="Android-Gradle">
|
||||
<configuration>
|
||||
<option name="GRADLE_PROJECT_PATH" value=":app" />
|
||||
</configuration>
|
||||
</facet>
|
||||
<facet type="android" name="Android">
|
||||
<configuration>
|
||||
<option name="SELECTED_BUILD_VARIANT" value="debug" />
|
||||
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
|
||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
|
||||
<afterSyncTasks>
|
||||
<task>generateDebugSources</task>
|
||||
</afterSyncTasks>
|
||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
|
||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
|
||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$/../../java/src">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../java/src" isTestSource="false" />
|
||||
</content>
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/google-services/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.1.1/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-analytics/8.4.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.google.android.gms/play-services-basement/8.4.0/jars" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/mockable-android-23.jar" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="seamless-xml-1.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="seamless-util-1.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="eventbus-2.4.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="cling-core-2.0.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-v4-23.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="play-services-analytics-8.4.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="support-annotations-23.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="seamless-http-1.1.0" level="project" />
|
||||
<orderEntry type="library" exported="" name="appcompat-v7-23.1.1" level="project" />
|
||||
<orderEntry type="library" exported="" name="play-services-basement-8.4.0" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
55
integrations/Android/proj/app/build.gradle
Normal file
@@ -0,0 +1,55 @@
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.2"
|
||||
defaultConfig {
|
||||
applicationId "com.zerotier.one"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 23
|
||||
versionCode 112
|
||||
versionName "1.1.2"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug {
|
||||
jniDebuggable true
|
||||
}
|
||||
}
|
||||
sourceSets.main {
|
||||
jniLibs.srcDir '../../java/libs' //set jniLibs directory to ./libs
|
||||
jni.srcDirs = [] //disable automatic ndk-build call
|
||||
java.srcDirs = ['src/main/java', '../../java/src']
|
||||
}
|
||||
task ndkBuild(type: Exec) {
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
commandLine "${android.getNdkDirectory().getAbsolutePath()}/ndk-build.cmd", '-j8', '-C', file('../../java').absolutePath, "ZT1=${System.getProperty('user.dir')}/../../.."
|
||||
} else {
|
||||
commandLine "${android.getNdkDirectory().getAbsolutePath()}/ndk-build", '-j8', '-C', file('../../java').absolutePath, "ZT1=${System.getProperty('user.dir')}/../../.."
|
||||
}
|
||||
}
|
||||
tasks.withType(JavaCompile) {
|
||||
compileTask -> compileTask.dependsOn ndkBuild
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url "http://teleal.org/m2"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(include: ['*.jar'], dir: 'libs')
|
||||
compile 'com.android.support:appcompat-v7:23.1.1'
|
||||
compile 'de.greenrobot:eventbus:2.4.0'
|
||||
compile 'com.android.support:support-v4:23.1.1'
|
||||
compile 'org.fourthline.cling:cling-core:2.0.1'
|
||||
compile 'com.google.android.gms:play-services-analytics:8.4.0'
|
||||
}
|
||||
1
integrations/Android/proj/app/google-services.json
Normal file
@@ -0,0 +1 @@
|
||||
{"project_info":{"project_id":"zerotier-one","project_number":"877961003667","name":"ZeroTier One"},"client":[{"client_info":{"client_id":"android:com.zerotier.one","client_type":1,"android_client_info":{"package_name":"com.zerotier.one"}},"oauth_client":[],"api_key":[],"services":{"analytics_service":{"status":2,"analytics_property":{"tracking_id":"UA-40176068-3"}},"cloud_messaging_service":{"status":1,"apns_config":[]},"appinvite_service":{"status":1,"other_platform_oauth_client":[]},"google_signin_service":{"status":1},"ads_service":{"status":1}}}],"ARTIFACT_VERSION":"1"}
|
||||
21
integrations/Android/proj/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\Users\Grant\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
-keepclassmembers class ** {
|
||||
public void onEvent*(**);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.zerotier.one;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
}
|
||||
}
|
||||
98
integrations/Android/proj/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.zerotier.one" >
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
|
||||
<application
|
||||
android:debuggable="true"
|
||||
android:name=".AnalyticsApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" >
|
||||
<service
|
||||
android:name=".service.ZeroTierOneService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_VPN_SERVICE" >
|
||||
<intent-filter>
|
||||
<action android:name="android.net.VpnService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name=".ui.NetworkListActivity"
|
||||
android:label="@string/app_name" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.JoinNetworkActivity"
|
||||
android:label="Join Network" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.PrefsActivity"
|
||||
android:label="@string/app_name" />
|
||||
|
||||
<receiver
|
||||
android:name=".service.StartupReceiver"
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Optionally, register AnalyticsReceiver and AnalyticsService to support background
|
||||
dispatching on non-Google Play devices -->
|
||||
<receiver android:name="com.google.android.gms.analytics.AnalyticsReceiver"
|
||||
android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.gms.analytics.ANALYTICS_DISPATCH" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service android:name="com.google.android.gms.analytics.AnalyticsService"
|
||||
android:enabled="true"
|
||||
android:exported="false"/>
|
||||
|
||||
<!-- Optionally, register CampaignTrackingReceiver and CampaignTrackingService to enable
|
||||
installation campaign reporting -->
|
||||
<receiver android:name="com.google.android.gms.analytics.CampaignTrackingReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.vending.INSTALL_REFERRER" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service android:name="com.google.android.gms.analytics.CampaignTrackingService" />
|
||||
|
||||
<!--
|
||||
<receiver
|
||||
android:name=".service.NetworkStateReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
-->
|
||||
|
||||
<service
|
||||
android:name=".service.RuntimeService"
|
||||
android:enabled="true"
|
||||
android:exported="true" >
|
||||
</service>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.version"
|
||||
android:value="@integer/google_play_services_version" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
BIN
integrations/Android/proj/app/src/main/ic_launcher-web.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,15 @@
|
||||
package Netcon;
|
||||
|
||||
/**
|
||||
* Created by Joseph on 3/14/16.
|
||||
*/
|
||||
public class NetconWrapper {
|
||||
public native int loadsymbols();
|
||||
|
||||
// From OneServiceSetup.cpp
|
||||
public native void startOneService();
|
||||
|
||||
static {
|
||||
System.loadLibrary("ZeroTierOneJNI");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.zerotier.one;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.google.android.gms.analytics.GoogleAnalytics;
|
||||
import com.google.android.gms.analytics.HitBuilders;
|
||||
import com.google.android.gms.analytics.Tracker;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Created by Grant on 8/25/2015.
|
||||
*/
|
||||
public class AnalyticsApplication extends Application {
|
||||
private Tracker mTracker;
|
||||
|
||||
/**
|
||||
* Gets the default {@link Tracker} for this {@link Application}.
|
||||
* @return tracker
|
||||
*/
|
||||
synchronized public Tracker getDefaultTracker() {
|
||||
if (mTracker == null) {
|
||||
SharedPreferences prefs = getSharedPreferences("user", MODE_PRIVATE);
|
||||
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String savedUUID = prefs.getString("uuid", UUID.randomUUID().toString());
|
||||
|
||||
if(savedUUID.equals(uuid)) {
|
||||
// this is a newly generated UUID. Save it
|
||||
SharedPreferences.Editor e = prefs.edit();
|
||||
e.putString("uuid", savedUUID);
|
||||
e.apply();
|
||||
}
|
||||
|
||||
GoogleAnalytics analytics = GoogleAnalytics.getInstance(this);
|
||||
// To enable debug logging use: adb shell setprop log.tag.GAv4 DEBUG
|
||||
mTracker = analytics.newTracker(R.xml.app_tracker);
|
||||
|
||||
mTracker.set("&uid", savedUUID);
|
||||
|
||||
mTracker.send(new HitBuilders.ScreenViewBuilder()
|
||||
.setNewSession()
|
||||
.build());
|
||||
}
|
||||
return mTracker;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.zerotier.one;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.google.android.gms.analytics.GoogleAnalytics;
|
||||
import com.google.android.gms.analytics.Tracker;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A collection of Google Analytics trackers. Fetch the tracker you need using
|
||||
* {@code AnalyticsTrackers.getInstance().get(...)}
|
||||
* <p/>
|
||||
* This code was generated by Android Studio but can be safely modified by
|
||||
* hand at this point.
|
||||
* <p/>
|
||||
* TODO: Call {@link #initialize(Context)} from an entry point in your app
|
||||
* before using this!
|
||||
*/
|
||||
public final class AnalyticsTrackers {
|
||||
|
||||
public enum Target {
|
||||
APP,
|
||||
// Add more trackers here if you need, and update the code in #get(Target) below
|
||||
}
|
||||
|
||||
private static AnalyticsTrackers sInstance;
|
||||
|
||||
public static synchronized void initialize(Context context) {
|
||||
if (sInstance != null) {
|
||||
throw new IllegalStateException("Extra call to initialize analytics trackers");
|
||||
}
|
||||
|
||||
sInstance = new AnalyticsTrackers(context);
|
||||
}
|
||||
|
||||
public static synchronized AnalyticsTrackers getInstance() {
|
||||
if (sInstance == null) {
|
||||
throw new IllegalStateException("Call initialize() before getInstance()");
|
||||
}
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private final Map<Target, Tracker> mTrackers = new HashMap<Target, Tracker>();
|
||||
private final Context mContext;
|
||||
|
||||
/**
|
||||
* Don't instantiate directly - use {@link #getInstance()} instead.
|
||||
*/
|
||||
private AnalyticsTrackers(Context context) {
|
||||
mContext = context.getApplicationContext();
|
||||
}
|
||||
|
||||
public synchronized Tracker get(Target target) {
|
||||
if (!mTrackers.containsKey(target)) {
|
||||
Tracker tracker;
|
||||
switch (target) {
|
||||
case APP:
|
||||
tracker = GoogleAnalytics.getInstance(mContext).newTracker(R.xml.app_tracker);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unhandled analytics target " + target);
|
||||
}
|
||||
mTrackers.put(target, tracker);
|
||||
}
|
||||
|
||||
return mTrackers.get(target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
import com.zerotier.sdk.ResultCode;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/23/2015.
|
||||
*/
|
||||
public class ErrorEvent {
|
||||
ResultCode result;
|
||||
|
||||
public ErrorEvent(ResultCode rc) {
|
||||
result = rc;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
import com.zerotier.one.ui.JoinNetworkFragment;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/23/2015.
|
||||
*/
|
||||
public class JoinNetworkEvent {
|
||||
private long networkId;
|
||||
|
||||
public JoinNetworkEvent(long nwid) {
|
||||
networkId = nwid;
|
||||
}
|
||||
|
||||
public long getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/23/2015.
|
||||
*/
|
||||
public class LeaveNetworkEvent {
|
||||
long networkId;
|
||||
|
||||
public LeaveNetworkEvent(long nwid) {
|
||||
networkId = nwid;
|
||||
}
|
||||
|
||||
public long getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
/**
|
||||
* Created by Grant on 8/4/2015.
|
||||
*/
|
||||
public class ManualDisconnectEvent {
|
||||
public ManualDisconnectEvent() {}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
import android.net.NetworkInfo;
|
||||
|
||||
import com.zerotier.sdk.VirtualNetworkConfig;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/23/2015.
|
||||
*/
|
||||
public class NetworkInfoReplyEvent {
|
||||
private VirtualNetworkConfig vnc;
|
||||
|
||||
public NetworkInfoReplyEvent(VirtualNetworkConfig vnc) {
|
||||
this.vnc = vnc;
|
||||
}
|
||||
|
||||
public VirtualNetworkConfig getNetworkInfo() {
|
||||
return vnc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
import com.zerotier.sdk.VirtualNetworkConfig;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/23/2015.
|
||||
*/
|
||||
public class NetworkListReplyEvent {
|
||||
private VirtualNetworkConfig[] networks;
|
||||
|
||||
public NetworkListReplyEvent(VirtualNetworkConfig[] networks) {
|
||||
this.networks = networks;
|
||||
}
|
||||
|
||||
public VirtualNetworkConfig[] getNetworkList() {
|
||||
return networks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
import android.net.Network;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/24/2015.
|
||||
*/
|
||||
public class NetworkReconfigureEvent {
|
||||
public NetworkReconfigureEvent() {}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/23/2015.
|
||||
*/
|
||||
public class NetworkRemovedEvent {
|
||||
private long networkId;
|
||||
|
||||
public NetworkRemovedEvent(long nwid) {
|
||||
networkId = nwid;
|
||||
}
|
||||
|
||||
public long getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
/**
|
||||
* Created by Grant on 7/28/2015.
|
||||
*/
|
||||
public class NodeDestroyedEvent {
|
||||
public NodeDestroyedEvent() {}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
/**
|
||||
* Created by Grant on 7/9/2015.
|
||||
*/
|
||||
public class NodeIDEvent {
|
||||
private long nodeId;
|
||||
|
||||
public NodeIDEvent(long nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
public long getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
import com.zerotier.sdk.NodeStatus;
|
||||
|
||||
/**
|
||||
* Created by Grant on 7/9/2015.
|
||||
*/
|
||||
public class NodeStatusEvent {
|
||||
private NodeStatus status;
|
||||
|
||||
public NodeStatusEvent(NodeStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public NodeStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/23/2015.
|
||||
*/
|
||||
public class RequestNetworkInfoEvent {
|
||||
private long networkId;
|
||||
|
||||
public RequestNetworkInfoEvent(long nwid) {
|
||||
networkId = nwid;
|
||||
}
|
||||
|
||||
public long getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/23/2015.
|
||||
*/
|
||||
public class RequestNetworkListEvent {
|
||||
public RequestNetworkListEvent() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
/**
|
||||
* Created by Grant on 8/1/2015.
|
||||
*/
|
||||
public class RequestNodeStatusEvent {
|
||||
public RequestNodeStatusEvent() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.zerotier.one.events;
|
||||
|
||||
/**
|
||||
* Created by Grant on 7/9/2015.
|
||||
*/
|
||||
public class StopEvent {
|
||||
public StopEvent() {}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
package com.zerotier.one.service;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ARPTable {
|
||||
public static final String TAG = "ARPTable";
|
||||
|
||||
private static final int REQUEST = 1;
|
||||
private static final int REPLY = 2;
|
||||
|
||||
private final HashMap<InetAddress, Long> inetAddressToMacAddress;
|
||||
private final HashMap<Long, InetAddress> macAddressToInetAdddress;
|
||||
|
||||
private static final long ENTRY_TIMEOUT = 120000L; // 2 minutes
|
||||
|
||||
private final ArrayList<ArpEntry> entries;
|
||||
|
||||
public ARPTable() {
|
||||
entries = new ArrayList<ArpEntry>();
|
||||
inetAddressToMacAddress = new HashMap<InetAddress, Long>();
|
||||
macAddressToInetAdddress = new HashMap<Long, InetAddress>();
|
||||
timeoutThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
timeoutThread.interrupt();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
void setAddress(InetAddress addr, long mac) {
|
||||
synchronized (inetAddressToMacAddress) {
|
||||
inetAddressToMacAddress.put(addr, mac);
|
||||
}
|
||||
synchronized (macAddressToInetAdddress) {
|
||||
macAddressToInetAdddress.put(mac, addr);
|
||||
}
|
||||
|
||||
synchronized (entries) {
|
||||
ArpEntry entry = new ArpEntry(mac, addr);
|
||||
if(entries.contains(entry)) {
|
||||
// update the entry time
|
||||
int index = entries.indexOf(entry);
|
||||
entries.get(index).updateTime();
|
||||
} else {
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateArpEntryTime(long mac) {
|
||||
synchronized (entries) {
|
||||
for(ArpEntry e : entries) {
|
||||
if(mac == e.getMac()) {
|
||||
e.updateTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateArpEntryTime(InetAddress addr) {
|
||||
synchronized (entries) {
|
||||
for(ArpEntry e : entries) {
|
||||
if(addr.equals(e.getAddress())) {
|
||||
e.updateTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MAC address for a given InetAddress
|
||||
*
|
||||
* @param addr address to get the MAC for
|
||||
* @return MAC address as long. -1 if the address isn't known
|
||||
*/
|
||||
long getMacForAddress(InetAddress addr) {
|
||||
synchronized (inetAddressToMacAddress) {
|
||||
if(inetAddressToMacAddress.containsKey(addr)) {
|
||||
long mac = inetAddressToMacAddress.get(addr);
|
||||
updateArpEntryTime(mac);
|
||||
return mac;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the InetAddress for a given MAC address
|
||||
*
|
||||
* @param mac mac address to lookup.
|
||||
* @return InetAddress if it's in the map. Otherwise null.
|
||||
*/
|
||||
InetAddress getAddressForMac(long mac) {
|
||||
synchronized (macAddressToInetAdddress) {
|
||||
if (macAddressToInetAdddress.containsKey(mac)) {
|
||||
InetAddress addr = macAddressToInetAdddress.get(mac);
|
||||
updateArpEntryTime(addr);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean hasMacForAddress(InetAddress addr) {
|
||||
synchronized (inetAddressToMacAddress) {
|
||||
return inetAddressToMacAddress.containsKey(addr);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasAddressForMac(long mac) {
|
||||
synchronized (macAddressToInetAdddress) {
|
||||
return macAddressToInetAdddress.containsKey(mac);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] longToBytes(long l) {
|
||||
// allocate an 8 byte buffer. Long.SIZE returns number of bits so divide by 8
|
||||
ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE/8);
|
||||
buffer.putLong(l);
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
public byte[] getRequestPacket(long senderMac, InetAddress senderAddress, InetAddress destinationAddress) {
|
||||
return getARPPacket(REQUEST, senderMac, 0, senderAddress, destinationAddress);
|
||||
}
|
||||
|
||||
public byte[] getReplyPacket(long senderMac, InetAddress senderAddress, long destMac, InetAddress destAddress) {
|
||||
return getARPPacket(REPLY, senderMac, destMac, senderAddress, destAddress);
|
||||
}
|
||||
|
||||
public byte[] getARPPacket(int packetType, long senderMac, long destMac, InetAddress senderAddress, InetAddress destinationAddress) {
|
||||
byte[] packet = new byte[28];
|
||||
|
||||
// Ethernet packet type
|
||||
packet[0] = 0;
|
||||
packet[1] = 1;
|
||||
|
||||
// IPV4 Protocol. 0x0800
|
||||
packet[2] = 0x08;
|
||||
packet[3] = 0;
|
||||
|
||||
// Hardware MAC address length
|
||||
packet[4] = 6;
|
||||
|
||||
// IP address length
|
||||
packet[5] = 4;
|
||||
|
||||
packet[6] = 0;
|
||||
packet[7] = (byte)packetType;
|
||||
|
||||
byte[] senderMacBuffer = longToBytes(senderMac);
|
||||
System.arraycopy(senderMacBuffer, 2, packet, 8, 6);
|
||||
|
||||
byte[] addrBuffer = senderAddress.getAddress();
|
||||
System.arraycopy(addrBuffer, 0, packet, 14, 4);
|
||||
|
||||
byte[] destMacBuffer = longToBytes(destMac);
|
||||
System.arraycopy(destMacBuffer, 2, packet, 18, 6);
|
||||
|
||||
byte[] destAddrBuffer = destinationAddress.getAddress();
|
||||
System.arraycopy(destAddrBuffer, 0, packet, 24, 4);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a reply is needed
|
||||
*
|
||||
* @param replyBuffer
|
||||
* @return
|
||||
*/
|
||||
public ARPReplyData processARPPacket(byte[] replyBuffer) {
|
||||
Log.d(TAG, "Processing ARP packet");
|
||||
|
||||
byte[] srcMacBuffer = new byte[8];
|
||||
System.arraycopy(replyBuffer, 8, srcMacBuffer, 2, 6);
|
||||
|
||||
byte[] senderAddressBuffer = new byte[4];
|
||||
System.arraycopy(replyBuffer, 14, senderAddressBuffer, 0, 4);
|
||||
|
||||
byte[] destMacBuffer = new byte[8];
|
||||
System.arraycopy(replyBuffer, 18, destMacBuffer, 2, 6);
|
||||
|
||||
byte[] destAddressBuffer = new byte[4];
|
||||
System.arraycopy(replyBuffer, 24, destAddressBuffer, 0, 4);
|
||||
|
||||
InetAddress senderAddress = null;
|
||||
try {
|
||||
senderAddress = InetAddress.getByAddress(senderAddressBuffer);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
InetAddress destAddress = null;
|
||||
try {
|
||||
destAddress = InetAddress.getByAddress(destAddressBuffer);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
long sourceMac = ByteBuffer.wrap(srcMacBuffer).getLong();
|
||||
long destMac = ByteBuffer.wrap(destMacBuffer).getLong();
|
||||
|
||||
if(sourceMac != 0 && senderAddress != null) {
|
||||
setAddress(senderAddress,sourceMac);
|
||||
}
|
||||
|
||||
if(destMac != 0 && destAddress != null) {
|
||||
setAddress(destAddress, destMac);
|
||||
}
|
||||
|
||||
|
||||
if(replyBuffer[7] == 1) {
|
||||
Log.d(TAG, "Reply needed");
|
||||
|
||||
ARPReplyData data = new ARPReplyData();
|
||||
data.destMac = sourceMac;
|
||||
data.destAddress = senderAddress;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public class ARPReplyData {
|
||||
public long senderMac;
|
||||
public InetAddress senderAddress;
|
||||
public long destMac;
|
||||
public InetAddress destAddress;
|
||||
}
|
||||
|
||||
private class ArpEntry {
|
||||
private long mac;
|
||||
private InetAddress address;
|
||||
private long time;
|
||||
|
||||
ArpEntry(long mac, InetAddress address) {
|
||||
this.mac = mac;
|
||||
this.address = address;
|
||||
updateTime();
|
||||
}
|
||||
|
||||
public long getMac() { return this.mac; }
|
||||
public InetAddress getAddress() { return this.address; }
|
||||
|
||||
public void updateTime() {
|
||||
this.time = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean equals(ArpEntry e) {
|
||||
// purposely do not check time
|
||||
// mac & address alone determine equality
|
||||
return (mac == e.mac) &&
|
||||
(address.equals(e.address));
|
||||
}
|
||||
}
|
||||
|
||||
private Thread timeoutThread = new Thread("ARP Timeout Thread") {
|
||||
public void run() {
|
||||
try {
|
||||
synchronized (entries) {
|
||||
for (ArpEntry e : entries) {
|
||||
if(e.time < (System.currentTimeMillis() + ENTRY_TIMEOUT) ) {
|
||||
synchronized(macAddressToInetAdddress) {
|
||||
macAddressToInetAdddress.remove(e.mac);
|
||||
}
|
||||
|
||||
synchronized (inetAddressToMacAddress) {
|
||||
inetAddressToMacAddress.remove(e.address);
|
||||
}
|
||||
|
||||
entries.remove(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.zerotier.one.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import com.zerotier.sdk.DataStoreGetListener;
|
||||
import com.zerotier.sdk.DataStorePutListener;
|
||||
|
||||
public class DataStore implements DataStoreGetListener, DataStorePutListener {
|
||||
private static final String TAG = "DataStore";
|
||||
private Context _ctx;
|
||||
|
||||
public DataStore(Context ctx) {
|
||||
this._ctx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onDataStorePut(String name, byte[] buffer, boolean secure) {
|
||||
Log.d(TAG, "Writing File: " + name);
|
||||
try {
|
||||
if(name.contains("/")) {
|
||||
// filename has a directory separator.
|
||||
int ix = name.lastIndexOf('/');
|
||||
String path = name.substring(0, ix);
|
||||
|
||||
File f = new File(_ctx.getFilesDir(), path);
|
||||
if(!f.exists()) {
|
||||
f.mkdirs();
|
||||
}
|
||||
|
||||
File outputFile = new File(f, name.substring(name.lastIndexOf("/")+1));
|
||||
FileOutputStream fos = new FileOutputStream(outputFile);
|
||||
fos.write(buffer);
|
||||
fos.flush();
|
||||
fos.close();
|
||||
return 0;
|
||||
} else {
|
||||
|
||||
FileOutputStream fos = _ctx.openFileOutput(name, Context.MODE_PRIVATE);
|
||||
fos.write(buffer);
|
||||
fos.flush();
|
||||
fos.close();
|
||||
return 0;
|
||||
}
|
||||
} catch (FileNotFoundException fnf) {
|
||||
fnf.printStackTrace();
|
||||
return -1;
|
||||
} catch (IOException io) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
io.printStackTrace(pw);
|
||||
Log.e(TAG, sw.toString());
|
||||
return -2;
|
||||
} catch(IllegalArgumentException ie) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
ie.printStackTrace(pw);
|
||||
Log.e(TAG, sw.toString());
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onDelete(String name) {
|
||||
Log.d(TAG, "Deleting File: " + name);
|
||||
boolean deleted = false;
|
||||
if(name.contains("/")) {
|
||||
// filename has a directory separator.
|
||||
|
||||
File f = new File(_ctx.getFilesDir(), name);
|
||||
if (!f.exists()) {
|
||||
deleted = true;
|
||||
} else {
|
||||
deleted = f.delete();
|
||||
}
|
||||
} else {
|
||||
deleted = _ctx.deleteFile(name);
|
||||
}
|
||||
return deleted ? 0 : 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long onDataStoreGet(String name, byte[] out_buffer,
|
||||
long bufferIndex, long[] out_objectSize) {
|
||||
Log.d(TAG, "Reading File: " + name);
|
||||
try {
|
||||
if(name.contains("/")) {
|
||||
// filename has a directory separator.
|
||||
int ix = name.lastIndexOf('/');
|
||||
String path = name.substring(0, ix);
|
||||
|
||||
File f = new File(_ctx.getFilesDir(), path);
|
||||
if(!f.exists()) {
|
||||
f.mkdirs();
|
||||
}
|
||||
|
||||
File inputFile = new File(f, name.substring(name.lastIndexOf('/')+1));
|
||||
if(!inputFile.exists()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FileInputStream fin = new FileInputStream(inputFile);
|
||||
if(bufferIndex > 0) {
|
||||
fin.skip(bufferIndex);
|
||||
}
|
||||
int read = fin.read(out_buffer);
|
||||
fin.close();
|
||||
return read;
|
||||
} else {
|
||||
FileInputStream fin = _ctx.openFileInput(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 -1;
|
||||
} catch (IOException io) {
|
||||
io.printStackTrace();
|
||||
return -2;
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.zerotier.one.service;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.util.Log;
|
||||
|
||||
import com.zerotier.one.events.StopEvent;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Created by Grant on 7/7/2015.
|
||||
*/
|
||||
public class NetworkStateReceiver extends BroadcastReceiver {
|
||||
private static String TAG = "NetworkStateReceiver";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context ctx, Intent intent) {
|
||||
ConnectivityManager cm =
|
||||
(ConnectivityManager)ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
|
||||
boolean isConnected = (activeNetwork != null &&
|
||||
activeNetwork.isConnectedOrConnecting());
|
||||
|
||||
Intent i = new Intent(ctx, RuntimeService.class);
|
||||
if(isConnected) {
|
||||
Log.d(TAG, "Network State: Connected");
|
||||
i.putExtra(RuntimeService.START_TYPE, RuntimeService.START_NETWORK_CHANGE);
|
||||
ctx.startService(i);
|
||||
} else {
|
||||
Log.d(TAG, "Network State: Disconnected");
|
||||
i.putExtra(RuntimeService.START_TYPE, RuntimeService.STOP_NETWORK_CHANGE);
|
||||
ctx.startService(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.zerotier.one.service;
|
||||
|
||||
import com.zerotier.one.util.InetAddressUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/13/2015.
|
||||
*/
|
||||
public class Route {
|
||||
InetAddress address;
|
||||
int prefix;
|
||||
|
||||
public Route(InetAddress address, int prefix) {
|
||||
this.address = address;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public boolean belongsToRoute(InetAddress otherAddr) {
|
||||
InetAddress route = InetAddressUtils.addressToRoute(otherAddr, prefix);
|
||||
|
||||
return address.equals(route);
|
||||
}
|
||||
|
||||
public boolean equals(Route r) {
|
||||
return address.equals(r.address) && prefix == r.prefix;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.zerotier.one.service;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.zerotier.one.events.ManualDisconnectEvent;
|
||||
import com.zerotier.one.events.StopEvent;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
public class RuntimeService extends Service {
|
||||
public static final String START_TYPE = "com.zerotier.one.service.RuntimeService.start_type";
|
||||
public static final int START_NETWORK_CHANGE = 1;
|
||||
public static final int START_BOOT = 2;
|
||||
public static final int START_USER_INTERFACE = 3;
|
||||
public static final int STOP_NETWORK_CHANGE = 4;
|
||||
public static final int STOP_USER_INTERFACE = 5;
|
||||
|
||||
private static final String TAG = "RuntimeService";
|
||||
private EventBus eventBus = EventBus.getDefault();
|
||||
|
||||
private boolean serviceStarted = false;
|
||||
|
||||
private NetworkStateReceiver nsReceiver = null;
|
||||
|
||||
public RuntimeService() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
// TODO: Return the communication channel to the service.
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.d(TAG, "RuntimeService started");
|
||||
|
||||
if(nsReceiver == null) {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
|
||||
nsReceiver = new NetworkStateReceiver();
|
||||
registerReceiver(nsReceiver, filter);
|
||||
}
|
||||
|
||||
if(intent == null) {
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
int startMode = intent.getIntExtra(START_TYPE, 0);
|
||||
// in each of the following cases, Prepare should always return null as the prepare call
|
||||
// is called the first time the UI is started granting permission for ZeroTier to use
|
||||
// the VPN API
|
||||
switch (startMode) {
|
||||
case START_NETWORK_CHANGE: {
|
||||
Log.d(TAG, "Start Network change");
|
||||
if(serviceStarted) {
|
||||
// start ZeroTierOne service.
|
||||
Log.d(TAG, "Start Network Change");
|
||||
Intent i = ZeroTierOneService.prepare(this);
|
||||
if(i == null) {
|
||||
i = new Intent(this, ZeroTierOneService.class);
|
||||
startService(i);
|
||||
serviceStarted = true;
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Ignore Start Network Change: Service has not been manually started.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case START_BOOT: {
|
||||
// if start on boot, start service
|
||||
Log.d(TAG, "Start Boot");
|
||||
Intent i = ZeroTierOneService.prepare(this);
|
||||
if(i == null) {
|
||||
i = new Intent(this, ZeroTierOneService.class);
|
||||
startService(i);
|
||||
serviceStarted = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case START_USER_INTERFACE: {
|
||||
Log.d(TAG, "Start User Interface");
|
||||
Intent i = ZeroTierOneService.prepare(this);
|
||||
if(i == null) {
|
||||
i = new Intent(this, ZeroTierOneService.class);
|
||||
startService(i);
|
||||
serviceStarted = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STOP_NETWORK_CHANGE: {
|
||||
Log.d(TAG, "Stop Network Change. Ignored.");
|
||||
// Intent i = new Intent(this, ZeroTierOneService.class);
|
||||
// stopService(i);
|
||||
// EventBus.getDefault().post(new StopEvent());
|
||||
break;
|
||||
}
|
||||
case STOP_USER_INTERFACE: {
|
||||
Log.d(TAG, "Stop User Interface");
|
||||
Intent i = new Intent(this, ZeroTierOneService.class);
|
||||
stopService(i);
|
||||
EventBus.getDefault().post(new ManualDisconnectEvent());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Log.e(TAG, "Unknown start ID: " + startId);
|
||||
break;
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.zerotier.one.service;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Broadcast receiver that listens for system bootup and starts
|
||||
* the ZeroTier service
|
||||
*/
|
||||
public class StartupReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "StartupReceiver";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context ctx, Intent intent) {
|
||||
Log.i(TAG, "Received: " + intent.getAction()+ ". Starting ZeroTier One service.");
|
||||
|
||||
// TODO: Start service
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
|
||||
boolean shouldStart = prefs.getBoolean("general_start_zerotier_on_boot", true);
|
||||
if(shouldStart) {
|
||||
Log.i(TAG, "Preferences set to start ZeroTier on boot");
|
||||
Intent i = new Intent(ctx, RuntimeService.class);
|
||||
i.putExtra(RuntimeService.START_TYPE, RuntimeService.START_BOOT);
|
||||
ctx.startService(i);
|
||||
} else {
|
||||
Log.i(TAG, "Preferences set to not start ZeroTier on boot");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package com.zerotier.one.service;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
import com.zerotier.one.util.IPPacketUtils;
|
||||
import com.zerotier.sdk.Node;
|
||||
import com.zerotier.sdk.ResultCode;
|
||||
import com.zerotier.sdk.VirtualNetworkConfig;
|
||||
import com.zerotier.sdk.VirtualNetworkFrameListener;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/3/2015.
|
||||
*/
|
||||
public class TunTapAdapter implements VirtualNetworkFrameListener {
|
||||
public static final String TAG = "TunTapAdapter";
|
||||
|
||||
private static final int ARP_PACKET = 0x0806;
|
||||
private static final int IPV4_PACKET = 0x0800;
|
||||
private static final int IPV6_PACKET = 0x86dd;
|
||||
|
||||
public static final long BROADCAST_MAC = 0xffffffffffffL;
|
||||
|
||||
private ARPTable arpTable;
|
||||
private Node node;
|
||||
private ZeroTierOneService ztService;
|
||||
private ParcelFileDescriptor vpnSocket;
|
||||
private FileInputStream in;
|
||||
private FileOutputStream out;
|
||||
|
||||
private final HashMap<Route, Long> routeMap;
|
||||
|
||||
private Thread receiveThread;
|
||||
|
||||
public TunTapAdapter(ZeroTierOneService ztService) {
|
||||
this.ztService = ztService;
|
||||
arpTable = new ARPTable();
|
||||
routeMap = new HashMap<Route, Long>();
|
||||
}
|
||||
|
||||
public void setNode(Node node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public void setVpnSocket(ParcelFileDescriptor vpnSocket) {
|
||||
this.vpnSocket = vpnSocket;
|
||||
}
|
||||
|
||||
public void setFileStreams(FileInputStream in, FileOutputStream out) {
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
public void addRouteAndNetwork(Route route, long networkId) {
|
||||
synchronized (routeMap) {
|
||||
routeMap.put(route, networkId);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearRouteMap() {
|
||||
synchronized (routeMap) {
|
||||
routeMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void startThreads() {
|
||||
receiveThread = new Thread("Tunnel Receive Thread") {
|
||||
public void run() {
|
||||
try {
|
||||
Log.d(TAG, "TUN Receive Thread Started");
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(32767);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
try {
|
||||
while (!isInterrupted()) {
|
||||
boolean idle = true;
|
||||
int length = in.read(buffer.array());
|
||||
if (length > 0) {
|
||||
Log.d(TAG, "Sending packet to ZeroTier. " + length + " bytes.");
|
||||
idle = false;
|
||||
|
||||
byte packet[] = new byte[length];
|
||||
System.arraycopy(buffer.array(), 0, packet, 0, length);
|
||||
|
||||
InetAddress destAddress = IPPacketUtils.getDestIP(packet);
|
||||
InetAddress sourceAddress = IPPacketUtils.getSourceIP(packet);
|
||||
|
||||
long nwid = networkIdForDestination(destAddress);
|
||||
if (nwid == 0) {
|
||||
Log.e(TAG, "Unable to find network ID for destination address: " + destAddress);
|
||||
continue;
|
||||
}
|
||||
|
||||
VirtualNetworkConfig cfg = node.networkConfig(nwid);
|
||||
InetAddress myAddress = cfg.assignedAddresses()[0].getAddress();
|
||||
|
||||
long srcMac = cfg.macAddress();
|
||||
long bgt_dl[] = new long[1];
|
||||
|
||||
if (arpTable.hasMacForAddress(destAddress)) {
|
||||
long destMac = arpTable.getMacForAddress(destAddress);
|
||||
|
||||
ResultCode rc = node.processVirtualNetworkFrame(
|
||||
System.currentTimeMillis(),
|
||||
nwid,
|
||||
srcMac,
|
||||
destMac,
|
||||
IPV4_PACKET,
|
||||
0,
|
||||
packet,
|
||||
bgt_dl);
|
||||
|
||||
if (rc != ResultCode.RESULT_OK) {
|
||||
Log.e(TAG, "Error calling processVirtualNetworkFrame: " + rc.toString());
|
||||
} else {
|
||||
Log.d(TAG, "Packet sent to ZT");
|
||||
ztService.setNextBackgroundTaskDeadline(bgt_dl[0]);
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Unknown dest MAC address. Need to look it up. " + destAddress);
|
||||
byte[] arpRequest = arpTable.getRequestPacket(srcMac, myAddress, destAddress);
|
||||
|
||||
ResultCode rc = node.processVirtualNetworkFrame(
|
||||
System.currentTimeMillis(),
|
||||
nwid,
|
||||
srcMac,
|
||||
BROADCAST_MAC,
|
||||
ARP_PACKET,
|
||||
0,
|
||||
arpRequest,
|
||||
bgt_dl);
|
||||
|
||||
if (rc != ResultCode.RESULT_OK) {
|
||||
Log.e(TAG, "Error sending ARP packet: " + rc.toString());
|
||||
} else {
|
||||
Log.d(TAG, "ARP Request Sent!");
|
||||
ztService.setNextBackgroundTaskDeadline(bgt_dl[0]);
|
||||
}
|
||||
}
|
||||
buffer.clear();
|
||||
} else {
|
||||
//Log.d(TAG, "No bytes read: " + length);
|
||||
}
|
||||
|
||||
if (idle) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error in TUN Receive: " + e.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// swallow InterruptedException
|
||||
}
|
||||
Log.d(TAG, "TUN Receive Thread ended");
|
||||
}
|
||||
};
|
||||
|
||||
receiveThread.start();
|
||||
}
|
||||
|
||||
public void interrupt() {
|
||||
if (receiveThread != null) {
|
||||
receiveThread.interrupt();
|
||||
try {
|
||||
receiveThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
// swallow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
if (receiveThread == null) {
|
||||
return false;
|
||||
} else {
|
||||
return receiveThread.isAlive();
|
||||
}
|
||||
}
|
||||
|
||||
public void onVirtualNetworkFrame(
|
||||
long nwid,
|
||||
long srcMac,
|
||||
long destMac,
|
||||
long etherType,
|
||||
long vlanId,
|
||||
byte[] frameData) {
|
||||
Log.d(TAG, "Got Virtual Network Frame. Network ID: " + Long.toHexString(nwid) + " Source MAC: " + Long.toHexString(srcMac) +
|
||||
" Dest MAC: " + Long.toHexString(destMac) + " Ether type: " + etherType + " VLAN ID: " + vlanId + " Frame Length: " + frameData.length);
|
||||
|
||||
if (vpnSocket == null) {
|
||||
Log.e(TAG, "vpnSocket is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (in == null || out == null) {
|
||||
Log.e(TAG, "no in/out streams");
|
||||
return;
|
||||
}
|
||||
|
||||
if (etherType == ARP_PACKET) {
|
||||
Log.d(TAG, "Got ARP Packet");
|
||||
|
||||
ARPTable.ARPReplyData data = arpTable.processARPPacket(frameData);
|
||||
|
||||
if (data != null && data.destMac != 0 && data.destAddress != null) {
|
||||
// We need to reply here.
|
||||
long deadline[] = new long[1];
|
||||
|
||||
VirtualNetworkConfig cfg = node.networkConfig(nwid);
|
||||
|
||||
InetAddress myAddress = cfg.assignedAddresses()[0].getAddress();
|
||||
|
||||
byte[] replyPacket = arpTable.getReplyPacket(cfg.macAddress(), myAddress, data.destMac, data.destAddress);
|
||||
ResultCode rc = node.processVirtualNetworkFrame(
|
||||
System.currentTimeMillis(),
|
||||
nwid,
|
||||
cfg.macAddress(),
|
||||
srcMac,
|
||||
ARP_PACKET,
|
||||
0,
|
||||
replyPacket,
|
||||
deadline);
|
||||
|
||||
if (rc != ResultCode.RESULT_OK) {
|
||||
Log.e(TAG, "Error sending ARP packet: " + rc.toString());
|
||||
} else {
|
||||
Log.d(TAG, "ARP Reply Sent!");
|
||||
ztService.setNextBackgroundTaskDeadline(deadline[0]);
|
||||
}
|
||||
}
|
||||
|
||||
} else if ((etherType == IPV4_PACKET) ||
|
||||
(etherType == IPV6_PACKET)) {
|
||||
// Got IPv4 or IPv6 packet!
|
||||
Log.d(TAG, "Got IP packet. Length: " + frameData.length + " Bytes");
|
||||
|
||||
try {
|
||||
InetAddress sourceAddress = IPPacketUtils.getSourceIP(frameData);
|
||||
if (sourceAddress != null) {
|
||||
arpTable.setAddress(sourceAddress, srcMac);
|
||||
}
|
||||
out.write(frameData);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error writing data to vpn socket: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Unknown Packet Type Received: 0x" + String.format("%02X%02X", frameData[12], frameData[13]));
|
||||
}
|
||||
}
|
||||
|
||||
private long networkIdForDestination(InetAddress destination) {
|
||||
synchronized (routeMap) {
|
||||
for (Route r : routeMap.keySet()) {
|
||||
if (r.belongsToRoute(destination)) {
|
||||
return routeMap.get(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.zerotier.one.service;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.zerotier.sdk.Node;
|
||||
import com.zerotier.sdk.PacketSender;
|
||||
import com.zerotier.sdk.ResultCode;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/3/2015.
|
||||
*/
|
||||
public class UdpCom implements PacketSender, Runnable {
|
||||
private final static String TAG = "UdpCom";
|
||||
|
||||
Node node;
|
||||
ZeroTierOneService ztService;
|
||||
DatagramSocket svrSocket;
|
||||
|
||||
UdpCom(ZeroTierOneService service, DatagramSocket socket) {
|
||||
svrSocket = socket;
|
||||
this.ztService = service;
|
||||
}
|
||||
|
||||
public void setNode(Node node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public int onSendPacketRequested(
|
||||
InetSocketAddress localAddress,
|
||||
InetSocketAddress remoteAddress,
|
||||
byte[] packetData,
|
||||
int ttl) {
|
||||
// TTL is ignored. No way to set it on a UDP packet in Java
|
||||
if(svrSocket == null) {
|
||||
Log.e(TAG, "Attempted to send packet on a null socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
DatagramPacket p = new DatagramPacket(packetData, packetData.length, remoteAddress);
|
||||
Log.d(TAG, "onSendPacketRequested: Sent "+ p.getLength() + " bytes to " + remoteAddress.toString());
|
||||
svrSocket.send(p);
|
||||
} catch (Exception ex) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
Log.d(TAG, "UDP Listen Thread Started.");
|
||||
try {
|
||||
long[] bgtask = new long[1];
|
||||
byte[] buffer = new byte[16384];
|
||||
while(!Thread.interrupted()) {
|
||||
|
||||
bgtask[0] = 0;
|
||||
DatagramPacket p = new DatagramPacket(buffer, buffer.length);
|
||||
|
||||
try {
|
||||
svrSocket.receive(p);
|
||||
if(p.getLength() > 0)
|
||||
{
|
||||
byte[] received = new byte[p.getLength()];
|
||||
System.arraycopy(p.getData(), 0, received, 0, p.getLength());
|
||||
Log.d(TAG, "Got " + p.getLength() + " Bytes From: " + p.getAddress().toString() +":" + p.getPort());
|
||||
|
||||
ResultCode rc = node.processWirePacket(System.currentTimeMillis(), null, new InetSocketAddress(p.getAddress(), p.getPort()), received, bgtask);
|
||||
|
||||
if(rc != ResultCode.RESULT_OK) {
|
||||
Log.e(TAG, "procesWirePacket returned: " + rc.toString());
|
||||
ztService.shutdown();
|
||||
}
|
||||
|
||||
ztService.setNextBackgroundTaskDeadline( bgtask[0] );
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
Log.d(TAG, "Socket Timeout");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Log.d(TAG, "UDP Listen Thread Ended.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,537 @@
|
||||
package com.zerotier.one.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.VpnService;
|
||||
import android.content.Intent;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.zerotier.one.events.ErrorEvent;
|
||||
import com.zerotier.one.events.JoinNetworkEvent;
|
||||
import com.zerotier.one.events.LeaveNetworkEvent;
|
||||
import com.zerotier.one.events.ManualDisconnectEvent;
|
||||
import com.zerotier.one.events.NetworkInfoReplyEvent;
|
||||
import com.zerotier.one.events.NetworkListReplyEvent;
|
||||
import com.zerotier.one.events.NetworkReconfigureEvent;
|
||||
import com.zerotier.one.events.NetworkRemovedEvent;
|
||||
import com.zerotier.one.events.NodeDestroyedEvent;
|
||||
import com.zerotier.one.events.NodeIDEvent;
|
||||
import com.zerotier.one.events.NodeStatusEvent;
|
||||
import com.zerotier.one.events.RequestNetworkInfoEvent;
|
||||
import com.zerotier.one.events.RequestNetworkListEvent;
|
||||
import com.zerotier.one.events.RequestNodeStatusEvent;
|
||||
import com.zerotier.one.events.StopEvent;
|
||||
import com.zerotier.one.util.InetAddressUtils;
|
||||
import com.zerotier.one.util.NetworkIdUtils;
|
||||
import com.zerotier.sdk.Event;
|
||||
import com.zerotier.sdk.EventListener;
|
||||
import com.zerotier.sdk.Node;
|
||||
import com.zerotier.sdk.NodeException;
|
||||
import com.zerotier.sdk.NodeStatus;
|
||||
import com.zerotier.sdk.ResultCode;
|
||||
import com.zerotier.sdk.VirtualNetworkConfig;
|
||||
import com.zerotier.sdk.VirtualNetworkConfigListener;
|
||||
import com.zerotier.sdk.VirtualNetworkConfigOperation;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
public class ZeroTierOneService extends VpnService implements Runnable, EventListener, VirtualNetworkConfigListener {
|
||||
|
||||
private static final String TAG = "ZT1_Service";
|
||||
|
||||
public static final String ZT1_NETWORK_ID = "com.zerotier.one.network_id";
|
||||
|
||||
public static final int MSG_JOIN_NETWORK = 1;
|
||||
public static final int MSG_LEAVE_NETWORK = 2;
|
||||
|
||||
private Thread vpnThread;
|
||||
private UdpCom udpCom;
|
||||
private Thread udpThread;
|
||||
|
||||
private TunTapAdapter tunTapAdapter;
|
||||
|
||||
private Node node;
|
||||
private DataStore dataStore;
|
||||
|
||||
DatagramSocket svrSocket;
|
||||
ParcelFileDescriptor vpnSocket;
|
||||
FileInputStream in;
|
||||
FileOutputStream out;
|
||||
|
||||
private final HashMap<Long, VirtualNetworkConfig> networkConfigs;
|
||||
|
||||
private EventBus eventBus = EventBus.getDefault();
|
||||
|
||||
private long nextBackgroundTaskDeadline = 0;
|
||||
private long lastMulticastGroupCheck = 0;
|
||||
|
||||
protected void setNextBackgroundTaskDeadline(long deadline) {
|
||||
synchronized (this) {
|
||||
nextBackgroundTaskDeadline = deadline;
|
||||
}
|
||||
}
|
||||
|
||||
public ZeroTierOneService() {
|
||||
super();
|
||||
dataStore = new DataStore(this);
|
||||
networkConfigs = new HashMap<>();
|
||||
eventBus.register(this);
|
||||
|
||||
Netcon.NetconWrapper wrapper = new Netcon.NetconWrapper();
|
||||
|
||||
|
||||
wrapper.startOneService();
|
||||
/*
|
||||
if(wrapper.loadsymbols() == 4)
|
||||
{
|
||||
Log.e(TAG,"loadsymbols(): Symbol found");
|
||||
//Toast t = Toast.makeText(this, "WORKED", Toast.LENGTH_SHORT);
|
||||
}
|
||||
else {
|
||||
Log.e(TAG,"loadsymbols(): Symbol NOT found");
|
||||
//Toast t = Toast.makeText(this, "DIDNT WORK", Toast.LENGTH_SHORT);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
|
||||
|
||||
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
public void stopZeroTier() {
|
||||
if(udpThread != null && udpThread.isAlive()) {
|
||||
udpThread.interrupt();
|
||||
udpThread = null;
|
||||
}
|
||||
|
||||
if(vpnThread != null && vpnThread.isAlive()) {
|
||||
vpnThread.interrupt();
|
||||
vpnThread = null;
|
||||
}
|
||||
|
||||
if(svrSocket != null) {
|
||||
svrSocket.close();
|
||||
svrSocket = null;
|
||||
}
|
||||
|
||||
if(node != null) {
|
||||
eventBus.post(new NodeDestroyedEvent());
|
||||
node.close();
|
||||
node = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
stopZeroTier();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevoke() {
|
||||
Intent i = new Intent(this, RuntimeService.class);
|
||||
i.putExtra(RuntimeService.START_TYPE, RuntimeService.STOP_USER_INTERFACE);
|
||||
this.startService(i);
|
||||
|
||||
stopZeroTier();
|
||||
try {
|
||||
vpnSocket.close();
|
||||
} catch (Exception e) {
|
||||
// swallow it
|
||||
}
|
||||
vpnSocket = null;
|
||||
|
||||
stopSelf();
|
||||
|
||||
Intent stopIntent = new Intent(this, RuntimeService.class);
|
||||
stopService(stopIntent);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
/*
|
||||
Log.d(TAG, "ZeroTierOne Service Started");
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
boolean autoRejoin = prefs.getBoolean("network_auto_rejoin_networks", true);
|
||||
|
||||
if(autoRejoin) {
|
||||
// find all local network configs and join them
|
||||
File networksFolder = new File(getFilesDir(), "networks.d");
|
||||
if (networksFolder.exists()) {
|
||||
File[] networks = networksFolder.listFiles();
|
||||
for (File f : networks) {
|
||||
if (f.getName().endsWith(".conf")) {
|
||||
String filename = f.getName();
|
||||
filename = filename.substring(0, filename.lastIndexOf('.'));
|
||||
Log.d(TAG, "Loading network: " + filename);
|
||||
ResultCode rc = node.join(NetworkIdUtils.hexStringToLong(filename));
|
||||
if (rc != ResultCode.RESULT_OK) {
|
||||
Log.d(TAG, "Error joining network: " + rc.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "This Node Address: " + Long.toHexString(node.address()));
|
||||
|
||||
while(!Thread.interrupted()) {
|
||||
try {
|
||||
|
||||
long dl = nextBackgroundTaskDeadline;
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if (dl <= now) {
|
||||
long[] returnDeadline = {0};
|
||||
ResultCode rc = node.processBackgroundTasks(now, returnDeadline);
|
||||
|
||||
synchronized(this) {
|
||||
nextBackgroundTaskDeadline = returnDeadline[0];
|
||||
}
|
||||
|
||||
if(rc != ResultCode.RESULT_OK) {
|
||||
Log.e(TAG, "Error on processBackgroundTasks: " + rc.toString());
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
long delay = (dl > now) ? (dl - now) : 100;
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException ie) {
|
||||
break;
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, ex.toString());
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "ZeroTierOne Service Ended");
|
||||
*/
|
||||
}
|
||||
|
||||
//
|
||||
// EventBus events
|
||||
//
|
||||
|
||||
public void onEvent(StopEvent e) {
|
||||
stopZeroTier();
|
||||
}
|
||||
|
||||
public void onEvent(ManualDisconnectEvent e) {
|
||||
stopZeroTier();
|
||||
}
|
||||
|
||||
public void onEventBackgroundThread(JoinNetworkEvent e) {
|
||||
/*
|
||||
Log.d(TAG, "Join Network Event");
|
||||
if(node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Remove this once multi-network support is in place
|
||||
for(long nwid : networkConfigs.keySet()) {
|
||||
onEventBackgroundThread(new LeaveNetworkEvent(nwid));
|
||||
}
|
||||
|
||||
networkConfigs.clear();
|
||||
|
||||
ResultCode rc = node.join(e.getNetworkId());
|
||||
if(rc != ResultCode.RESULT_OK) {
|
||||
eventBus.post(new ErrorEvent(rc));
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void onEventBackgroundThread(LeaveNetworkEvent e) {
|
||||
/*
|
||||
Log.d(TAG, "Leave Network Event");
|
||||
|
||||
if(node != null) {
|
||||
ResultCode rc = node.leave(e.getNetworkId());
|
||||
if (rc != ResultCode.RESULT_OK) {
|
||||
eventBus.post(new ErrorEvent(rc));
|
||||
return;
|
||||
}
|
||||
String certsFile = "networks.d/" + Long.toHexString(e.getNetworkId()) + ".mcerts";
|
||||
String confFile = "networks.d/" + Long.toHexString(e.getNetworkId()) + ".conf";
|
||||
dataStore.onDelete(confFile);
|
||||
dataStore.onDelete(certsFile);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void onEventBackgroundThread(RequestNetworkInfoEvent e) {
|
||||
/*
|
||||
if(node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
VirtualNetworkConfig vnc = node.networkConfig(e.getNetworkId());
|
||||
if(vnc != null) {
|
||||
eventBus.post(new NetworkInfoReplyEvent(vnc));
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void onEventBackgroundThread(RequestNetworkListEvent e) {
|
||||
/*
|
||||
if(node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
VirtualNetworkConfig[] networks = node.networks();
|
||||
if(networks != null && networks.length > 0) {
|
||||
eventBus.post(new NetworkListReplyEvent(networks));
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void onEventBackgroundThread(RequestNodeStatusEvent e) {
|
||||
/*
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeStatus ns = node.status();
|
||||
|
||||
eventBus.post(new NodeStatusEvent(ns));
|
||||
*/
|
||||
}
|
||||
|
||||
public void onEventAsync(NetworkReconfigureEvent e) {
|
||||
updateTunnelConfig();
|
||||
}
|
||||
|
||||
//
|
||||
// Event Listener Overrides
|
||||
//
|
||||
public void onEvent(Event e) {
|
||||
/*
|
||||
Log.d(TAG, "Event: " + e.toString());
|
||||
|
||||
if(node != null) {
|
||||
NodeStatus status = node.status();
|
||||
NodeStatusEvent nse = new NodeStatusEvent(status);
|
||||
eventBus.post(nse);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void onTrace(String msg) {
|
||||
Log.d(TAG, "Trace: " + msg);
|
||||
}
|
||||
|
||||
//
|
||||
// Virtual Network Config Listener overrides
|
||||
//
|
||||
|
||||
public int onNetworkConfigurationUpdated(
|
||||
long nwid,
|
||||
VirtualNetworkConfigOperation op,
|
||||
VirtualNetworkConfig config) {
|
||||
/*
|
||||
Log.d(TAG, "Virtual Network Config Operation: " + op.toString());
|
||||
switch(op) {
|
||||
case VIRTUAL_NETWORK_CONFIG_OPERATION_UP: {
|
||||
Log.d(TAG, "Network Type:" + config.networkType().toString() + " " +
|
||||
"Network Status: " + config.networkStatus().toString() + " " +
|
||||
"Network Name: " + config.name() + " ");
|
||||
|
||||
eventBus.post(new NetworkInfoReplyEvent(config));
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE: {
|
||||
Log.d(TAG, "Network Config Update!");
|
||||
|
||||
|
||||
VirtualNetworkConfig cfg = null;
|
||||
synchronized (networkConfigs) {
|
||||
if (networkConfigs.containsKey(nwid)) {
|
||||
cfg = networkConfigs.get(nwid);
|
||||
}
|
||||
}
|
||||
|
||||
if(cfg == null) {
|
||||
// we don't already have this network config
|
||||
Log.d(TAG, "Adding new network.");
|
||||
synchronized (networkConfigs) {
|
||||
networkConfigs.put(nwid, config);
|
||||
}
|
||||
eventBus.post(new NetworkReconfigureEvent());
|
||||
eventBus.post(new NetworkInfoReplyEvent(config));
|
||||
break;
|
||||
}
|
||||
|
||||
if(!cfg.equals(config)) {
|
||||
Log.d(TAG, "Updating network config");
|
||||
synchronized (networkConfigs) {
|
||||
networkConfigs.remove(nwid);
|
||||
networkConfigs.put(nwid, config);
|
||||
}
|
||||
eventBus.post(new NetworkReconfigureEvent());
|
||||
}
|
||||
|
||||
eventBus.post(new NetworkInfoReplyEvent(config));
|
||||
}
|
||||
break;
|
||||
|
||||
case VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN:
|
||||
case VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY:
|
||||
Log.d(TAG, "Network Down!");
|
||||
synchronized (networkConfigs) {
|
||||
if (networkConfigs.containsKey(nwid)) {
|
||||
networkConfigs.remove(nwid);
|
||||
}
|
||||
}
|
||||
|
||||
eventBus.post(new NetworkReconfigureEvent());
|
||||
eventBus.post(new NetworkRemovedEvent(nwid));
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown Network Config Operation!");
|
||||
break;
|
||||
}
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected void shutdown() {
|
||||
stopZeroTier();
|
||||
this.stopSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should ONLY be called from onEventAsync(NetworkReconfigureEvent)
|
||||
*/
|
||||
private void updateTunnelConfig() {
|
||||
|
||||
/*
|
||||
synchronized (networkConfigs) {
|
||||
if (networkConfigs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tunTapAdapter.isRunning()) {
|
||||
tunTapAdapter.interrupt();
|
||||
}
|
||||
|
||||
tunTapAdapter.clearRouteMap();
|
||||
|
||||
if (vpnSocket != null) {
|
||||
try {
|
||||
vpnSocket.close();
|
||||
in.close();
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
vpnSocket = null;
|
||||
in = null;
|
||||
out = null;
|
||||
}
|
||||
|
||||
Builder builder = new Builder();
|
||||
|
||||
int highestMtu = 0;
|
||||
|
||||
for (VirtualNetworkConfig config : networkConfigs.values()) {
|
||||
if (config.isEnabled()) {
|
||||
long nwid = config.networkId();
|
||||
|
||||
InetSocketAddress addresses[] = config.assignedAddresses();
|
||||
|
||||
int adi = 0;
|
||||
|
||||
for (int i = 0; i < addresses.length; ++i) {
|
||||
Log.d(TAG, "Adding VPN Address: " + addresses[i].getAddress() + " Mac: " + Long.toHexString(config.macAddress()));
|
||||
|
||||
byte[] addrBytes = addresses[i].getAddress().getAddress();
|
||||
|
||||
try {
|
||||
adi = ByteBuffer.wrap(addrBytes).getInt();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Exception calculating multicast ADI: " + e.getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
int routeSub = addresses[i].getPort();
|
||||
InetAddress address = addresses[i].getAddress();
|
||||
|
||||
// TODO: Support IPv6
|
||||
if(address instanceof Inet6Address) {
|
||||
Log.d(TAG, "Got an IPV6 Address. Not adding it to the adapter");
|
||||
continue;
|
||||
}
|
||||
|
||||
InetAddress route = InetAddressUtils.addressToRoute(address, routeSub);
|
||||
if(route == null) {
|
||||
Log.e(TAG, "NULL route calculated!");
|
||||
continue;
|
||||
}
|
||||
|
||||
ResultCode rc = node.multicastSubscribe(nwid, TunTapAdapter.BROADCAST_MAC, adi);
|
||||
if (rc != ResultCode.RESULT_OK) {
|
||||
Log.e(TAG, "Error joining multicast group");
|
||||
} else {
|
||||
Log.d(TAG, "Joined multicast group");
|
||||
}
|
||||
|
||||
builder.addAddress(address, routeSub);
|
||||
builder.addRoute(route, routeSub);
|
||||
|
||||
Route r = new Route(route, routeSub);
|
||||
tunTapAdapter.addRouteAndNetwork(r, nwid);
|
||||
|
||||
|
||||
int mtu = config.mtu();
|
||||
if (mtu > highestMtu) {
|
||||
highestMtu = mtu;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.setMtu(highestMtu);
|
||||
builder.setSession("ZeroTier One");
|
||||
|
||||
|
||||
vpnSocket = builder.establish();
|
||||
if(vpnSocket == null) {
|
||||
Log.e(TAG, "vpnSocket is NULL after builder.establish()!!!!");
|
||||
stopZeroTier();
|
||||
return;
|
||||
}
|
||||
|
||||
in = new FileInputStream(vpnSocket.getFileDescriptor());
|
||||
out = new FileOutputStream(vpnSocket.getFileDescriptor());
|
||||
|
||||
tunTapAdapter.setVpnSocket(vpnSocket);
|
||||
tunTapAdapter.setFileStreams(in, out);
|
||||
tunTapAdapter.startThreads();
|
||||
Log.i(TAG, "ZeroTier One Connected");
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/**
|
||||
* Copyright 2013 Maarten Pennings
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* If you use this software in a product, an acknowledgment in the product
|
||||
* documentation would be appreciated but is not required.
|
||||
*/
|
||||
|
||||
package com.zerotier.one.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.inputmethodservice.Keyboard;
|
||||
import android.inputmethodservice.KeyboardView;
|
||||
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnFocusChangeListener;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
|
||||
/**
|
||||
* When an activity hosts a keyboardView, this class allows several EditText's to register for it.
|
||||
*
|
||||
* @author Maarten Pennings
|
||||
* @date 2012 December 23
|
||||
*/
|
||||
class HexKeyboard {
|
||||
private final static String TAG = "HexKeyboard";
|
||||
|
||||
/** A link to the KeyboardView that is used to render this CustomKeyboard. */
|
||||
private KeyboardView mKeyboardView;
|
||||
/** A link to the activity that hosts the {@link #mKeyboardView}. */
|
||||
private Activity mHostActivity;
|
||||
|
||||
/** The key (code) handler. */
|
||||
private OnKeyboardActionListener mOnKeyboardActionListener = new OnKeyboardActionListener() {
|
||||
|
||||
public final static int CodeDelete = -5; // Keyboard.KEYCODE_DELETE
|
||||
public final static int CodeCancel = -3; // Keyboard.KEYCODE_CANCEL
|
||||
public final static int CodePrev = 55000;
|
||||
public final static int CodeAllLeft = 55001;
|
||||
public final static int CodeLeft = 55002;
|
||||
public final static int CodeRight = 55003;
|
||||
public final static int CodeAllRight = 55004;
|
||||
public final static int CodeNext = 55005;
|
||||
public final static int CodeClear = 55006;
|
||||
|
||||
@Override
|
||||
public void onKey(int primaryCode, int[] keyCodes) {
|
||||
// NOTE We can say '<Key android:codes="49,50" ... >' in the xml file; all codes come in keyCodes, the first in this list in primaryCode
|
||||
// Get the EditText and its Editable
|
||||
View focusCurrent = mHostActivity.getWindow().getCurrentFocus();
|
||||
|
||||
if( focusCurrent==null) return;
|
||||
|
||||
EditText edittext = (EditText) focusCurrent;
|
||||
Editable editable = edittext.getText();
|
||||
int start = edittext.getSelectionStart();
|
||||
// Apply the key to the edittext
|
||||
if( primaryCode==CodeCancel ) {
|
||||
hideCustomKeyboard();
|
||||
} else if( primaryCode==CodeDelete ) {
|
||||
if( editable!=null && start>0 ) editable.delete(start - 1, start);
|
||||
} else if( primaryCode==CodeClear ) {
|
||||
if( editable!=null ) editable.clear();
|
||||
} else if( primaryCode==CodeLeft ) {
|
||||
if( start>0 ) edittext.setSelection(start - 1);
|
||||
} else if( primaryCode==CodeRight ) {
|
||||
if (start < edittext.length()) edittext.setSelection(start + 1);
|
||||
} else if( primaryCode==CodeAllLeft ) {
|
||||
edittext.setSelection(0);
|
||||
} else if( primaryCode==CodeAllRight ) {
|
||||
edittext.setSelection(edittext.length());
|
||||
} else if( primaryCode==CodePrev ) {
|
||||
View focusNew= edittext.focusSearch(View.FOCUS_LEFT);
|
||||
if( focusNew!=null ) focusNew.requestFocus();
|
||||
} else if( primaryCode==CodeNext ) {
|
||||
View focusNew= edittext.focusSearch(View.FOCUS_RIGHT);
|
||||
if( focusNew!=null ) focusNew.requestFocus();
|
||||
} else { // insert character
|
||||
editable.insert(start, Character.toString((char) primaryCode));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPress(int arg0) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRelease(int primaryCode) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onText(CharSequence text) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swipeDown() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swipeLeft() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swipeRight() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void swipeUp() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a custom keyboard, that uses the KeyboardView (with resource id <var>viewid</var>) of the <var>host</var> activity,
|
||||
* and load the keyboard layout from xml file <var>layoutid</var> (see {@link Keyboard} for description).
|
||||
* Note that the <var>host</var> activity must have a <var>KeyboardView</var> in its layout (typically aligned with the bottom of the activity).
|
||||
* Note that the keyboard layout xml file may include key codes for navigation; see the constants in this class for their values.
|
||||
* Note that to enable EditText's to use this custom keyboard, call the {@link #registerEditText(int)}.
|
||||
*
|
||||
* @param host The hosting activity.
|
||||
* @param viewid The id of the KeyboardView.
|
||||
* @param layoutid The id of the xml file containing the keyboard layout.
|
||||
*/
|
||||
public HexKeyboard(Activity host, int viewid, int layoutid) {
|
||||
mHostActivity= host;
|
||||
mKeyboardView= (KeyboardView)mHostActivity.findViewById(viewid);
|
||||
mKeyboardView.setKeyboard(new Keyboard(mHostActivity, layoutid));
|
||||
mKeyboardView.setPreviewEnabled(false); // NOTE Do not show the preview balloons
|
||||
mKeyboardView.setOnKeyboardActionListener(mOnKeyboardActionListener);
|
||||
// Hide the standard keyboard initially
|
||||
mHostActivity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
}
|
||||
|
||||
/** Returns whether the CustomKeyboard is visible. */
|
||||
public boolean isCustomKeyboardVisible() {
|
||||
return mKeyboardView.getVisibility() == View.VISIBLE;
|
||||
}
|
||||
|
||||
/** Make the CustomKeyboard visible, and hide the system keyboard for view v. */
|
||||
public void showCustomKeyboard( View v ) {
|
||||
mKeyboardView.setVisibility(View.VISIBLE);
|
||||
mKeyboardView.setEnabled(true);
|
||||
if( v!=null ) ((InputMethodManager)mHostActivity.getSystemService(Activity.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
/** Make the CustomKeyboard invisible. */
|
||||
public void hideCustomKeyboard() {
|
||||
mKeyboardView.setVisibility(View.GONE);
|
||||
mKeyboardView.setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register <var>EditText<var> with resource id <var>resid</var> (on the hosting activity) for using this custom keyboard.
|
||||
*
|
||||
* @param resid The resource id of the EditText that registers to the custom keyboard.
|
||||
*/
|
||||
public void registerEditText(int resid) {
|
||||
// Find the EditText 'resid'
|
||||
EditText edittext= (EditText)mHostActivity.findViewById(resid);
|
||||
// Make the custom keyboard appear
|
||||
edittext.setOnFocusChangeListener(new OnFocusChangeListener() {
|
||||
// NOTE By setting the on focus listener, we can show the custom keyboard when the edit box gets focus, but also hide it when the edit box loses focus
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (hasFocus) showCustomKeyboard(v);
|
||||
else hideCustomKeyboard();
|
||||
}
|
||||
});
|
||||
edittext.setOnClickListener(new OnClickListener() {
|
||||
// NOTE By setting the on click listener, we can show the custom keyboard again, by tapping on an edit box that already had focus (but that had the keyboard hidden).
|
||||
@Override public void onClick(View v) {
|
||||
showCustomKeyboard(v);
|
||||
}
|
||||
});
|
||||
// Disable standard keyboard hard way
|
||||
// NOTE There is also an easy way: 'edittext.setInputType(InputType.TYPE_NULL)' (but you will not have a cursor, and no 'edittext.setCursorVisible(true)' doesn't work )
|
||||
edittext.setOnTouchListener(new OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
EditText edittext = (EditText) v;
|
||||
int inType = edittext.getInputType(); // Backup the input type
|
||||
edittext.setInputType(InputType.TYPE_NULL); // Disable standard keyboard
|
||||
edittext.onTouchEvent(event); // Call native handler
|
||||
edittext.setInputType(inType); // Restore input type
|
||||
return true; // Consume touch event
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// Disable spell check (hex strings look like words to Android)
|
||||
edittext.setInputType(edittext.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.zerotier.one.ui;
|
||||
|
||||
import android.app.Fragment;
|
||||
|
||||
/**
|
||||
* Created by Grant on 5/20/2015.
|
||||
*/
|
||||
public class JoinNetworkActivity extends SingleFragmentActivity {
|
||||
public Fragment createFragment() {
|
||||
return new JoinNetworkFragment();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package com.zerotier.one.ui;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import com.google.android.gms.analytics.HitBuilders;
|
||||
import com.google.android.gms.analytics.Tracker;
|
||||
|
||||
import com.zerotier.one.AnalyticsApplication;
|
||||
import com.zerotier.one.R;
|
||||
import com.zerotier.one.events.JoinNetworkEvent;
|
||||
import com.zerotier.one.util.NetworkIdUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
/**
|
||||
* Created by Grant on 5/20/2015.
|
||||
*/
|
||||
public class JoinNetworkFragment extends Fragment {
|
||||
public final static String TAG = "JoinNetwork";
|
||||
|
||||
private Tracker tracker = null;
|
||||
|
||||
private Button mJoinButton;
|
||||
private EditText mNetworkIdTextView;
|
||||
private ListView mRecentNetworksList;
|
||||
|
||||
EventBus eventBus = EventBus.getDefault();
|
||||
|
||||
HexKeyboard mHexKeyboard;
|
||||
|
||||
public JoinNetworkFragment() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if(tracker == null) {
|
||||
tracker = ((AnalyticsApplication) getActivity().getApplication()).getDefaultTracker();
|
||||
}
|
||||
|
||||
tracker.setScreenName("Join Network");
|
||||
tracker.send(new HitBuilders.ScreenViewBuilder().build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if(tracker == null) {
|
||||
tracker = ((AnalyticsApplication) getActivity().getApplication()).getDefaultTracker();
|
||||
}
|
||||
|
||||
tracker.setScreenName("Join Network");
|
||||
tracker.send(new HitBuilders.ScreenViewBuilder().build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
mHexKeyboard = new HexKeyboard(getActivity(), R.id.join_network_keyboard, R.xml.hex_keyboard);
|
||||
mHexKeyboard.registerEditText(R.id.join_network_edit_text);
|
||||
mHexKeyboard.hideCustomKeyboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, parent, savedInstanceState);
|
||||
|
||||
View v = inflater.inflate(R.layout.fragment_join_network, parent, false);
|
||||
|
||||
mNetworkIdTextView = (EditText)v.findViewById(R.id.join_network_edit_text);
|
||||
mNetworkIdTextView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
if (s.toString().length() == 16) {
|
||||
mJoinButton.setEnabled(true);
|
||||
} else {
|
||||
mJoinButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
mJoinButton = (Button)v.findViewById(R.id.button_join_network);
|
||||
mJoinButton.setEnabled(false);
|
||||
mJoinButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
try {
|
||||
Log.d(TAG, "Joining network " + mNetworkIdTextView.getText().toString());
|
||||
String netIdString = mNetworkIdTextView.getText().toString();
|
||||
long networkId = NetworkIdUtils.hexStringToLong(netIdString);
|
||||
|
||||
SharedPreferences prefs = getActivity().getSharedPreferences("recent_networks", Context.MODE_PRIVATE);
|
||||
try {
|
||||
String nws = prefs.getString("recent_networks", (new JSONArray()).toString());
|
||||
JSONArray jArray = new JSONArray(nws);
|
||||
ArrayList<String> array = new ArrayList<>();
|
||||
|
||||
// convert the JSON array to an actual array for ease of modification
|
||||
for(int i = 0; i < jArray.length(); ++i) {
|
||||
array.add(jArray.getString(i));
|
||||
}
|
||||
|
||||
boolean containsValue = false;
|
||||
|
||||
for(String id : array) {
|
||||
if(id.equals(netIdString)) {
|
||||
containsValue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(containsValue) {
|
||||
// remove the item
|
||||
array.remove(netIdString);
|
||||
} else {
|
||||
// pop off the last item
|
||||
if (array.size() > 4) {
|
||||
array.remove(4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// insert ID at the beginning of the list
|
||||
array.add(0, mNetworkIdTextView.getText().toString());
|
||||
|
||||
// convert the list back to a JSON array
|
||||
jArray = new JSONArray();
|
||||
for(String id : array) {
|
||||
jArray.put(id);
|
||||
}
|
||||
|
||||
// write JSON array to preferences
|
||||
SharedPreferences.Editor e = prefs.edit();
|
||||
e.putString("recent_networks", jArray.toString());
|
||||
e.apply();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Exception setting recent networks: " + e.getMessage());
|
||||
}
|
||||
eventBus.post(new JoinNetworkEvent(networkId));
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
Log.d(TAG, "Join Network: Error parsing network ID");
|
||||
} finally {
|
||||
getActivity().onBackPressed();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mRecentNetworksList = (ListView) v.findViewById(R.id.recent_networks_list);
|
||||
|
||||
SharedPreferences prefs = getActivity().getSharedPreferences("recent_networks", Context.MODE_PRIVATE);
|
||||
try {
|
||||
String nws = prefs.getString("recent_networks", new JSONArray().toString());
|
||||
JSONArray networks = new JSONArray(nws);
|
||||
|
||||
|
||||
TextView recentNetworksText = (TextView) v.findViewById(R.id.recent_networks);
|
||||
|
||||
if (networks.length() == 0) {
|
||||
mRecentNetworksList.setVisibility(View.GONE);
|
||||
recentNetworksText.setVisibility(View.GONE);
|
||||
} else {
|
||||
mRecentNetworksList.setVisibility(View.VISIBLE);
|
||||
recentNetworksText.setVisibility(View.VISIBLE);
|
||||
|
||||
ArrayList<String> recentNetworks = new ArrayList<>();
|
||||
for(int i = 0; i < networks.length(); ++i) {
|
||||
recentNetworks.add(networks.getString(i));
|
||||
}
|
||||
|
||||
final NetworkIDAdapter adapter = new NetworkIDAdapter(recentNetworks);
|
||||
mRecentNetworksList.setAdapter(adapter);
|
||||
|
||||
mRecentNetworksList.setOnItemClickListener(new ListView.OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
mNetworkIdTextView.setText(adapter.getItem(position));
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "JSON Error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
private class NetworkIDAdapter extends ArrayAdapter<String> {
|
||||
public NetworkIDAdapter(ArrayList<String> config) {
|
||||
super(getActivity(), 0, config);
|
||||
Log.d(TAG, "Created network list item adapter");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if(convertView == null) {
|
||||
convertView = getActivity().getLayoutInflater().inflate(R.layout.list_item_recent_network, null);
|
||||
}
|
||||
|
||||
String networkId = getItem(position);
|
||||
TextView network = (TextView) convertView.findViewById(R.id.list_recent_network_id);
|
||||
network.setText(networkId);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.zerotier.one.ui;
|
||||
|
||||
import android.app.Fragment;
|
||||
|
||||
/**
|
||||
* Created by Grant on 5/20/2015.
|
||||
*/
|
||||
public class NetworkListActivity extends SingleFragmentActivity {
|
||||
@Override
|
||||
public Fragment createFragment() {
|
||||
return new NetworkListFragment();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,487 @@
|
||||
package com.zerotier.one.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.gms.analytics.HitBuilders;
|
||||
import com.google.android.gms.analytics.Tracker;
|
||||
import com.zerotier.one.AnalyticsApplication;
|
||||
import com.zerotier.one.R;
|
||||
import com.zerotier.one.events.LeaveNetworkEvent;
|
||||
import com.zerotier.one.events.ManualDisconnectEvent;
|
||||
import com.zerotier.one.events.NetworkInfoReplyEvent;
|
||||
import com.zerotier.one.events.NetworkListReplyEvent;
|
||||
import com.zerotier.one.events.NetworkRemovedEvent;
|
||||
import com.zerotier.one.events.NodeDestroyedEvent;
|
||||
import com.zerotier.one.events.NodeIDEvent;
|
||||
import com.zerotier.one.events.NodeStatusEvent;
|
||||
import com.zerotier.one.events.RequestNetworkListEvent;
|
||||
import com.zerotier.one.events.RequestNodeStatusEvent;
|
||||
import com.zerotier.one.events.StopEvent;
|
||||
import com.zerotier.one.service.RuntimeService;
|
||||
import com.zerotier.one.service.ZeroTierOneService;
|
||||
import com.zerotier.sdk.NodeStatus;
|
||||
import com.zerotier.sdk.VirtualNetworkConfig;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
public class NetworkListFragment extends Fragment {
|
||||
enum ConnectStatus {
|
||||
CONNECTED,
|
||||
DISCONNECTED
|
||||
}
|
||||
|
||||
private Tracker tracker = null;
|
||||
|
||||
public static final String TAG = "NetworkListFragment";
|
||||
|
||||
public static final int START_VPN = 2;
|
||||
|
||||
private ArrayList<VirtualNetworkConfig> mNetworkConfigs;
|
||||
|
||||
private EventBus eventBus;
|
||||
private NetworkAdapter adapter;
|
||||
private ListView listView;
|
||||
private TextView nodeIdView;
|
||||
private TextView nodeStatusView;
|
||||
private Button connectButton;
|
||||
private MenuItem joinNetworkMenu;
|
||||
|
||||
private ConnectStatus cstatus = ConnectStatus.DISCONNECTED;
|
||||
|
||||
/**
|
||||
* Mandatory empty constructor for the fragment manager to instantiate the
|
||||
* fragment (e.g. upon screen orientation changes).
|
||||
*/
|
||||
public NetworkListFragment() {
|
||||
Log.d(TAG, "Network List Fragment created");
|
||||
mNetworkConfigs = new ArrayList<VirtualNetworkConfig>();
|
||||
eventBus = EventBus.getDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
eventBus.register(this);
|
||||
eventBus.post(new RequestNetworkListEvent());
|
||||
eventBus.post(new RequestNodeStatusEvent());
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
eventBus.unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, parent, savedInstanceState);
|
||||
|
||||
View v = inflater.inflate(R.layout.network_list_fragment, parent, false);
|
||||
|
||||
listView = (ListView)v.findViewById(R.id.joined_networks_list);
|
||||
|
||||
adapter = new NetworkAdapter(mNetworkConfigs);
|
||||
listView.setAdapter(adapter);
|
||||
|
||||
nodeIdView = (TextView) v.findViewById(R.id.node_id);
|
||||
nodeStatusView = (TextView) v.findViewById(R.id.node_status);
|
||||
connectButton = (Button) v.findViewById(R.id.connect_button);
|
||||
|
||||
if(cstatus == ConnectStatus.CONNECTED) {
|
||||
connectButton.setText(getResources().getText(R.string.button_disconnect));
|
||||
} else {
|
||||
connectButton.setText(getResources().getText(R.string.button_connect));
|
||||
}
|
||||
|
||||
connectButton.setOnClickListener(new Button.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
if (cstatus == ConnectStatus.CONNECTED) {
|
||||
Intent i = new Intent(getActivity(), RuntimeService.class);
|
||||
i.putExtra(RuntimeService.START_TYPE, RuntimeService.STOP_USER_INTERFACE);
|
||||
getActivity().startService(i);
|
||||
cstatus = ConnectStatus.DISCONNECTED;
|
||||
nodeStatusView.setText("OFFLINE");
|
||||
connectButton.setText(getResources().getText(R.string.button_connect));
|
||||
|
||||
} else {
|
||||
sendStartServiceIntent();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
private void sendStartServiceIntent() {
|
||||
Intent i = ZeroTierOneService.prepare(getActivity());
|
||||
if(i != null) {
|
||||
startActivityForResult(i, START_VPN);
|
||||
} else {
|
||||
Log.d(TAG, "Intent is NULL. Already approved.");
|
||||
startService();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
Log.d(TAG, "NetworkListFragment.onCreate");
|
||||
super.onCreate(savedInstanceState);
|
||||
PreferenceManager.setDefaultValues(getActivity(), R.xml.preferences, false);
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
if(tracker == null) {
|
||||
tracker = ((AnalyticsApplication) getActivity().getApplication()).getDefaultTracker();
|
||||
}
|
||||
|
||||
tracker.setScreenName("Network List");
|
||||
tracker.send(new HitBuilders.ScreenViewBuilder().build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
cstatus = ConnectStatus.DISCONNECTED;
|
||||
connectButton.setText(getResources().getText(R.string.button_connect));
|
||||
nodeStatusView.setText("OFFLINE");
|
||||
mNetworkConfigs.clear();
|
||||
sortNetworkListAndNotify();
|
||||
|
||||
eventBus.post(new RequestNetworkListEvent());
|
||||
eventBus.post(new RequestNodeStatusEvent());
|
||||
|
||||
if(tracker == null) {
|
||||
tracker = ((AnalyticsApplication) getActivity().getApplication()).getDefaultTracker();
|
||||
}
|
||||
tracker.setScreenName("Network List");
|
||||
tracker.send(new HitBuilders.ScreenViewBuilder().build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
Log.d(TAG, "NetworkListFragment.onCreateOptionsMenu");
|
||||
inflater.inflate(R.menu.menu_network_list, menu);
|
||||
joinNetworkMenu = menu.findItem(R.id.menu_item_join_network);
|
||||
joinNetworkMenu.setEnabled(false);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
eventBus.post(new RequestNodeStatusEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_item_join_network: {
|
||||
Log.d(TAG, "Selected Join Network");
|
||||
Intent i = new Intent(getActivity(), JoinNetworkActivity.class);
|
||||
startActivity(i);
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_item_prefs: {
|
||||
Log.d(TAG, "Selected Preferences");
|
||||
Intent i = new Intent(getActivity(), PrefsActivity.class);
|
||||
startActivity(i);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
|
||||
switch(requestCode)
|
||||
{
|
||||
case START_VPN:
|
||||
{
|
||||
// Start ZeroTierOneService
|
||||
startService();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
Log.d(TAG, "NetworkListFragment.onAttach");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
Log.d(TAG, "NetworkListFragment.onDetach");
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
public void onEventMainThread(NetworkListReplyEvent e) {
|
||||
Log.d(TAG, "Got network list");
|
||||
mNetworkConfigs.clear();
|
||||
for(int i = 0; i < e.getNetworkList().length; ++i) {
|
||||
mNetworkConfigs.add(e.getNetworkList()[i]);
|
||||
}
|
||||
|
||||
sortNetworkListAndNotify();
|
||||
}
|
||||
|
||||
public void onEventMainThread(NetworkInfoReplyEvent e) {
|
||||
Log.d(TAG, "Got Network Info");
|
||||
VirtualNetworkConfig vnc = e.getNetworkInfo();
|
||||
boolean hasNetworkWithId = false;
|
||||
|
||||
for(VirtualNetworkConfig c : mNetworkConfigs) {
|
||||
if(c.networkId() == vnc.networkId()) {
|
||||
hasNetworkWithId = true;
|
||||
int index = mNetworkConfigs.indexOf(c);
|
||||
mNetworkConfigs.set(index, vnc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!hasNetworkWithId) {
|
||||
mNetworkConfigs.add(vnc);
|
||||
}
|
||||
|
||||
sortNetworkListAndNotify();
|
||||
}
|
||||
|
||||
public void onEventMainThread(NetworkRemovedEvent e) {
|
||||
Log.d(TAG, "Removing network: " + Long.toHexString(e.getNetworkId()));
|
||||
for(VirtualNetworkConfig c : mNetworkConfigs) {
|
||||
if(c.networkId() == e.getNetworkId()) {
|
||||
mNetworkConfigs.remove(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sortNetworkListAndNotify();
|
||||
}
|
||||
|
||||
public void onEventMainThread(NodeIDEvent e) {
|
||||
long nodeId = e.getNodeId();
|
||||
String nodeHex = Long.toHexString(nodeId);
|
||||
while(nodeHex.length() < 10) {
|
||||
nodeHex = "0" + nodeHex;
|
||||
}
|
||||
nodeIdView.setText(nodeHex);
|
||||
}
|
||||
|
||||
public void onEventMainThread(NodeStatusEvent e) {
|
||||
NodeStatus status = e.getStatus();
|
||||
if(status.isOnline()) {
|
||||
nodeStatusView.setText("ONLINE");
|
||||
if(joinNetworkMenu != null) {
|
||||
joinNetworkMenu.setEnabled(true);
|
||||
}
|
||||
cstatus = ConnectStatus.CONNECTED;
|
||||
if(connectButton != null) {
|
||||
connectButton.setText(getResources().getText(R.string.button_disconnect));
|
||||
}
|
||||
if(nodeIdView != null) {
|
||||
nodeIdView.setText(Long.toHexString(status.getAddres()));
|
||||
}
|
||||
} else {
|
||||
setOfflineState();
|
||||
}
|
||||
}
|
||||
|
||||
public void onEventMainThread(NodeDestroyedEvent e) {
|
||||
setOfflineState();
|
||||
}
|
||||
|
||||
private void setOfflineState() {
|
||||
if(nodeStatusView != null) {
|
||||
nodeStatusView.setText("OFFLINE");
|
||||
}
|
||||
if(joinNetworkMenu != null) {
|
||||
joinNetworkMenu.setEnabled(false);
|
||||
}
|
||||
cstatus = ConnectStatus.DISCONNECTED;
|
||||
if(connectButton != null) {
|
||||
connectButton.setText(getResources().getText(R.string.button_connect));
|
||||
}
|
||||
}
|
||||
|
||||
private void sortNetworkListAndNotify() {
|
||||
Collections.sort(mNetworkConfigs);
|
||||
adapter.notifyDataSetChanged();
|
||||
listView.invalidateViews();
|
||||
}
|
||||
|
||||
private void startService() {
|
||||
Intent i = new Intent(getActivity(), RuntimeService.class);
|
||||
i.putExtra(RuntimeService.START_TYPE, RuntimeService.START_USER_INTERFACE);
|
||||
getActivity().startService(i);
|
||||
}
|
||||
|
||||
private class NetworkAdapter extends ArrayAdapter<VirtualNetworkConfig> {
|
||||
|
||||
public NetworkAdapter(ArrayList<VirtualNetworkConfig> config) {
|
||||
super(getActivity(), 0, config);
|
||||
Log.d(TAG, "Created network list item adapter");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if(!listView.getItemsCanFocus()) {
|
||||
listView.setItemsCanFocus(true);
|
||||
}
|
||||
|
||||
if(convertView == null) {
|
||||
convertView = getActivity().getLayoutInflater().inflate(R.layout.list_item_network, null);
|
||||
}
|
||||
|
||||
VirtualNetworkConfig vnc = getItem(position);
|
||||
TextView networkId = (TextView)convertView.findViewById(R.id.network_list_network_id);
|
||||
networkId.setText(Long.toHexString(vnc.networkId()));
|
||||
|
||||
TextView networkName = (TextView) convertView.findViewById(R.id.network_list_network_name);
|
||||
networkName.setText(vnc.name());
|
||||
|
||||
|
||||
TextView networkStatus = (TextView)convertView.findViewById(R.id.network_status_textview);
|
||||
|
||||
CharSequence statusText;
|
||||
switch(vnc.networkStatus()) {
|
||||
case NETWORK_STATUS_OK:
|
||||
statusText = getResources().getText(R.string.network_status_ok);
|
||||
break;
|
||||
case NETWORK_STATUS_ACCESS_DENIED:
|
||||
statusText = getResources().getText(R.string.network_status_access_denied);
|
||||
break;
|
||||
case NETWORK_STATUS_CLIENT_TOO_OLD:
|
||||
statusText = getResources().getText(R.string.network_status_client_too_old);
|
||||
break;
|
||||
case NETWORK_STATUS_NOT_FOUND:
|
||||
statusText = getResources().getText(R.string.network_status_not_found);
|
||||
break;
|
||||
case NETWORK_STATUS_PORT_ERROR:
|
||||
statusText = getResources().getText(R.string.network_status_port_error);
|
||||
break;
|
||||
case NETWORK_STATUS_REQUESTING_CONFIGURATION:
|
||||
statusText = getResources().getText(R.string.network_status_requesting_configuration);
|
||||
break;
|
||||
default:
|
||||
statusText = getResources().getText(R.string.network_status_unknown);
|
||||
break;
|
||||
}
|
||||
networkStatus.setText(statusText);
|
||||
|
||||
TextView networkType = (TextView)convertView.findViewById(R.id.network_type_textview);
|
||||
switch(vnc.networkType()) {
|
||||
case NETWORK_TYPE_PUBLIC:
|
||||
networkType.setText(getResources().getText(R.string.network_type_public));
|
||||
break;
|
||||
case NETWORK_TYPE_PRIVATE:
|
||||
networkType.setText(getResources().getText(R.string.network_type_private));
|
||||
break;
|
||||
default:
|
||||
networkType.setText(getResources().getText(R.string.network_type_unknown));
|
||||
}
|
||||
|
||||
String mac = Long.toHexString(vnc.macAddress());
|
||||
while(mac.length() < 12) {
|
||||
mac = "0" + mac;
|
||||
}
|
||||
|
||||
StringBuilder displayMac = new StringBuilder();
|
||||
displayMac.append(mac.charAt(0));
|
||||
displayMac.append(mac.charAt(1));
|
||||
displayMac.append(':');
|
||||
displayMac.append(mac.charAt(2));
|
||||
displayMac.append(mac.charAt(3));
|
||||
displayMac.append(':');
|
||||
displayMac.append(mac.charAt(4));
|
||||
displayMac.append(mac.charAt(5));
|
||||
displayMac.append(':');
|
||||
displayMac.append(mac.charAt(6));
|
||||
displayMac.append(mac.charAt(7));
|
||||
displayMac.append(':');
|
||||
displayMac.append(mac.charAt(8));
|
||||
displayMac.append(mac.charAt(9));
|
||||
displayMac.append(':');
|
||||
displayMac.append(mac.charAt(10));
|
||||
displayMac.append(mac.charAt(11));
|
||||
|
||||
TextView macView = (TextView)convertView.findViewById(R.id.network_mac_textview);
|
||||
macView.setText(displayMac.toString());
|
||||
|
||||
TextView mtuView = (TextView) convertView.findViewById(R.id.network_mtu_textview);
|
||||
mtuView.setText(Integer.toString(vnc.mtu()));
|
||||
|
||||
TextView broadcastEnabledView = (TextView) convertView.findViewById(R.id.network_broadcast_textview);
|
||||
broadcastEnabledView.setText(vnc.broadcastEnabled() ? "Enabled" : "Disabled");
|
||||
|
||||
TextView bridgingEnabledView = (TextView) convertView.findViewById(R.id.network_bridging_textview);
|
||||
bridgingEnabledView.setText(vnc.isBridgeEnabled() ? "Enabled" : "Disabled");
|
||||
|
||||
InetSocketAddress[] addresses = vnc.assignedAddresses();
|
||||
StringBuilder addrText = new StringBuilder();
|
||||
for(InetSocketAddress addr : addresses) {
|
||||
InetAddress ipaddr = addr.getAddress();
|
||||
if(ipaddr instanceof Inet6Address) {
|
||||
continue;
|
||||
}
|
||||
String address = addr.toString();
|
||||
if(address.startsWith("/")) {
|
||||
address = address.substring(1);
|
||||
}
|
||||
int lastSlashPosition = address.lastIndexOf(':');
|
||||
address = address.substring(0, lastSlashPosition) + "/" + address.substring(lastSlashPosition+1, address.length());
|
||||
addrText.append(address);
|
||||
addrText.append('\n');
|
||||
}
|
||||
|
||||
TextView addressesView = (TextView) convertView.findViewById(R.id.network_ipaddresses_textview);
|
||||
addressesView.setText(addrText.toString());
|
||||
|
||||
Button leaveButton = (Button) convertView.findViewById(R.id.network_leave_button);
|
||||
leaveButton.setOnClickListener(new LeaveButtonClickListener(vnc.networkId()));
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
private class LeaveButtonClickListener implements Button.OnClickListener {
|
||||
|
||||
private long nwid;
|
||||
|
||||
public LeaveButtonClickListener(long nwid) {
|
||||
this.nwid = nwid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Log.d(TAG, "Leave Button Pressed for network: " + Long.toHexString(nwid));
|
||||
EventBus.getDefault().post(new LeaveNetworkEvent(nwid));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.zerotier.one.ui;
|
||||
|
||||
import android.app.Fragment;
|
||||
|
||||
/**
|
||||
* Created by Grant on 7/7/2015.
|
||||
*/
|
||||
public class PrefsActivity extends SingleFragmentActivity {
|
||||
@Override
|
||||
public Fragment createFragment() {
|
||||
return new PrefsFragment();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.zerotier.one.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceFragment;
|
||||
|
||||
import com.google.android.gms.analytics.HitBuilders;
|
||||
import com.google.android.gms.analytics.Tracker;
|
||||
import com.zerotier.one.AnalyticsApplication;
|
||||
import com.zerotier.one.R;
|
||||
import com.zerotier.one.service.ZeroTierOneService;
|
||||
|
||||
/**
|
||||
* Created by Grant on 7/7/2015.
|
||||
*/
|
||||
public class PrefsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private Tracker tracker = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
if(tracker == null) {
|
||||
tracker = ((AnalyticsApplication) getActivity().getApplication()).getDefaultTracker();
|
||||
}
|
||||
|
||||
tracker.setScreenName("Preferences");
|
||||
tracker.send(new HitBuilders.ScreenViewBuilder().build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if(tracker == null) {
|
||||
tracker = ((AnalyticsApplication) getActivity().getApplication()).getDefaultTracker();
|
||||
}
|
||||
|
||||
tracker.setScreenName("Preferences");
|
||||
tracker.send(new HitBuilders.ScreenViewBuilder().build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if(key.equals("network_use_cellular_data")) {
|
||||
if(sharedPreferences.getBoolean("network_use_cellular_data", false)) {
|
||||
Intent i = new Intent(getActivity(), ZeroTierOneService.class);
|
||||
getActivity().startService(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.zerotier.one.ui;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.zerotier.one.R;
|
||||
|
||||
/**
|
||||
* Created by Grant on 5/16/2015.
|
||||
*/
|
||||
public abstract class SingleFragmentActivity extends AppCompatActivity {
|
||||
protected abstract Fragment createFragment();
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_fragment);
|
||||
|
||||
FragmentManager fm = getFragmentManager();
|
||||
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
|
||||
|
||||
if(fragment == null) {
|
||||
fragment = createFragment();
|
||||
fm.beginTransaction()
|
||||
.add(R.id.fragmentContainer, fragment)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.zerotier.one.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/19/2015.
|
||||
*/
|
||||
public class IPPacketUtils {
|
||||
private static final String TAG = "IPPacketUtils";
|
||||
|
||||
public static InetAddress getSourceIP(byte[] data) {
|
||||
byte[] addrBuffer = new byte[4];
|
||||
System.arraycopy(data, 12, addrBuffer, 0, 4);
|
||||
try {
|
||||
return InetAddress.getByAddress(addrBuffer);
|
||||
} catch (UnknownHostException e) {
|
||||
Log.e(TAG, "Error creating InetAddress: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static InetAddress getDestIP(byte[] data) {
|
||||
byte[] addrBuffer = new byte[4];
|
||||
System.arraycopy(data, 16, addrBuffer, 0, 4);
|
||||
try {
|
||||
return InetAddress.getByAddress(addrBuffer);
|
||||
} catch (UnknownHostException e) {
|
||||
Log.e(TAG, "Error creating InetAddress: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the 1's complement checksum of a region of bytes
|
||||
*
|
||||
* @param buffer
|
||||
* @param startValue
|
||||
* @param startByte
|
||||
* @param endByte
|
||||
* @return
|
||||
*/
|
||||
public static long calculateChecksum(byte[] buffer, long startValue, int startByte, int endByte) {
|
||||
|
||||
int length = endByte - startByte;
|
||||
int i = startByte;
|
||||
long sum = startValue;
|
||||
long data;
|
||||
|
||||
// Handle all pairs
|
||||
while (length > 1) {
|
||||
data = (((buffer[i] << 8) & 0xFF00) | ((buffer[i + 1]) & 0xFF));
|
||||
sum += data;
|
||||
// 1's complement carry bit correction in 16-bits (detecting sign extension)
|
||||
if ((sum & 0xFFFF0000) > 0) {
|
||||
sum = sum & 0xFFFF;
|
||||
sum += 1;
|
||||
}
|
||||
|
||||
i += 2;
|
||||
length -= 2;
|
||||
}
|
||||
|
||||
// Handle remaining byte in odd length buffers
|
||||
if (length > 0) {
|
||||
sum += (buffer[i] << 8 & 0xFF00);
|
||||
// 1's complement carry bit correction in 16-bits (detecting sign extension)
|
||||
if ((sum & 0xFFFF0000) > 0) {
|
||||
sum = sum & 0xFFFF;
|
||||
sum += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Final 1's complement value correction to 16-bits
|
||||
sum = ~sum;
|
||||
sum = sum & 0xFFFF;
|
||||
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.zerotier.one.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Created by Grant on 6/13/2015.
|
||||
*/
|
||||
public class InetAddressUtils {
|
||||
public static final String TAG = "InetAddressUtils";
|
||||
|
||||
public static byte[] addressToNetmask(InetAddress addr, int prefix) {
|
||||
int numAddressBytes = addr.getAddress().length;
|
||||
if(numAddressBytes > 4) {
|
||||
throw new UnsupportedOperationException("IPv6 is not yet supported");
|
||||
}
|
||||
int numAddressBits = numAddressBytes * 8;
|
||||
|
||||
byte[] maskBytes = new byte[numAddressBytes];
|
||||
for(int i = 0; i < numAddressBytes; ++i) {
|
||||
maskBytes[i] = (byte)255;
|
||||
}
|
||||
int mask = ByteBuffer.wrap(maskBytes).getInt();
|
||||
|
||||
mask = mask >> (numAddressBits - prefix);
|
||||
mask = mask << (numAddressBits - prefix);
|
||||
|
||||
return ByteBuffer.allocate(4).putInt(mask).array();
|
||||
}
|
||||
|
||||
public static InetAddress addressToRoute(InetAddress addr, int prefix) {
|
||||
byte[] maskBytes = addressToNetmask(addr, prefix);
|
||||
byte[] routeBytes = new byte[maskBytes.length];
|
||||
InetAddress route = null;
|
||||
try {
|
||||
for(int i = 0; i < maskBytes.length; ++i) {
|
||||
routeBytes[i] = (byte)(addr.getAddress()[i] & maskBytes[i]);
|
||||
}
|
||||
route = InetAddress.getByAddress(routeBytes);
|
||||
|
||||
} catch (UnknownHostException e) {
|
||||
Log.e(TAG, "Uknown Host Exception calculating route");
|
||||
}
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.zerotier.one.util;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Created by Grant on 5/26/2015.
|
||||
*/
|
||||
public class NetworkIdUtils {
|
||||
/**
|
||||
* Long will not parse a 64-bit hex string when the highest bit is 1 for some reasoon. So
|
||||
* for converting network IDs in hex to a long, we must use big integer.
|
||||
*
|
||||
* @param hexString
|
||||
* @return
|
||||
*/
|
||||
public static long hexStringToLong(String hexString) {
|
||||
BigInteger value = new BigInteger(hexString, 16);
|
||||
return value.longValue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" >
|
||||
<shape>
|
||||
<solid
|
||||
android:color="#ffb354" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#000000" />
|
||||
<corners
|
||||
android:radius="3dp" />
|
||||
<padding
|
||||
android:left="5dp"
|
||||
android:top="5dp"
|
||||
android:right="5dp"
|
||||
android:bottom="5dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:state_focused="true" >
|
||||
<shape>
|
||||
<solid
|
||||
android:color="#ffb354" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#000000" />
|
||||
<corners
|
||||
android:radius="3dp" />
|
||||
<padding
|
||||
android:left="5dp"
|
||||
android:top="5dp"
|
||||
android:right="5dp"
|
||||
android:bottom="5dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<shape>
|
||||
<solid
|
||||
android:color="#ffb354" />
|
||||
<stroke
|
||||
android:width="0dp"
|
||||
android:color="#000000" />
|
||||
<corners
|
||||
android:radius="3dp" />
|
||||
<padding
|
||||
android:left="5dp"
|
||||
android:top="5dp"
|
||||
android:right="5dp"
|
||||
android:bottom="5dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
BIN
integrations/Android/proj/app/src/main/res/drawable/hex.png
Normal file
|
After Width: | Height: | Size: 689 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 20 KiB |
BIN
integrations/Android/proj/app/src/main/res/drawable/txt.png
Normal file
|
After Width: | Height: | Size: 624 B |
@@ -0,0 +1,4 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fragmentContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fragmentJoinNetwork"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/network_id_textview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/network_id_string"
|
||||
android:textStyle="bold"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
style="?android:listSeparatorTextViewStyle"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/join_network_edit_text"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:digits="0123456789ABCDEF"
|
||||
android:inputType="textCapCharacters"
|
||||
android:layout_below="@id/network_id_textview"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:maxLength="16"
|
||||
android:textCursorDrawable="@null"
|
||||
android:backgroundTint="#ffb354"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_join_network"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_below="@id/join_network_edit_text"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:text="@string/join_network"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/recent_networks"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/recent_networks"
|
||||
android:textStyle="bold"
|
||||
android:layout_below="@id/button_join_network"
|
||||
style="?android:listSeparatorTextViewStyle"/>
|
||||
|
||||
<ListView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/recent_networks_list"
|
||||
android:layout_below="@+id/recent_networks"
|
||||
android:layout_centerHorizontal="true" />
|
||||
|
||||
<android.inputmethodservice.KeyboardView
|
||||
android:id="@+id/join_network_keyboard"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:focusable="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,176 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp">
|
||||
|
||||
<TextView android:id="@+id/network_list_network_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:text="Network ID"
|
||||
android:fontFamily="monospace"
|
||||
android:textColor="#555555" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/network_list_network_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingTop="4dp"
|
||||
android:text="Network Name"
|
||||
android:textColor="#000000" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@android:color/darker_gray"/>
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:columnCount="2"
|
||||
android:layout_margin="8dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end"
|
||||
android:stretchColumns="1">
|
||||
|
||||
<TableRow android:layout_width="fill_parent">
|
||||
<TextView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/network_status"
|
||||
android:paddingLeft="40dp"
|
||||
android:paddingRight="20dp"
|
||||
android:layout_gravity="right"/>
|
||||
|
||||
<TextView android:id="@+id/network_status_textview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text=""/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow android:layout_width="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/network_type"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingLeft="40dp"
|
||||
android:layout_gravity="right"/>
|
||||
|
||||
<TextView android:id="@+id/network_type_textview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text=""/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow android:layout_width="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/network_mac"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingLeft="40dp"
|
||||
android:layout_gravity="right"/>
|
||||
|
||||
<TextView android:id="@+id/network_mac_textview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text=""/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow android:layout_width="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/network_mtu"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingLeft="40dp"
|
||||
android:layout_gravity="right"/>
|
||||
|
||||
<TextView android:id="@+id/network_mtu_textview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text=""/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow android:layout_width="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/network_broadcast"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingLeft="40dp"
|
||||
android:layout_gravity="right"/>
|
||||
|
||||
<TextView android:id="@+id/network_broadcast_textview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text=""/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow android:layout_width="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/network_bridging"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingLeft="40dp"
|
||||
android:layout_gravity="right"/>
|
||||
|
||||
<TextView android:id="@+id/network_bridging_textview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text=""/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow android:layout_width="fill_parent">
|
||||
<TextView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/network_ips"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingLeft="40dp"
|
||||
android:layout_gravity="right"/>
|
||||
|
||||
<TextView android:id="@+id/network_ipaddresses_textview"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:gravity="right"
|
||||
android:text=""/>
|
||||
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<View android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/network_leave_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:text="@string/leave_button_text"
|
||||
/>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView android:id="@+id/list_recent_network_id"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/network_list_fragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<RelativeLayout
|
||||
android:id="@+id/status_bar"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="5dp">
|
||||
|
||||
<TextView android:id="@+id/node_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:text=""
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center"/>
|
||||
|
||||
<TextView android:id="@+id/node_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:text="OFFLINE"
|
||||
android:layout_toRightOf="@id/node_id"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/connect_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:padding="5dp"
|
||||
android:text="@string/button_connect"
|
||||
android:layout_alignParentRight="true"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<ListView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/status_bar"
|
||||
android:id="@+id/joined_networks_list"
|
||||
android:layout_alignParentTop="true" />
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:id="@+id/menu_item_join_network"
|
||||
android:icon="@android:drawable/ic_menu_add"
|
||||
android:title="@string/menu_join_network"
|
||||
app:showAsAction="ifRoom|withText"/>
|
||||
|
||||
<item android:id="@+id/menu_item_prefs"
|
||||
android:icon="@android:drawable/ic_menu_preferences"
|
||||
android:title="@string/settings"
|
||||
app:showAsAction="ifRoom|withText"/>
|
||||
</menu>
|
||||
|
After Width: | Height: | Size: 774 B |
|
After Width: | Height: | Size: 519 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.5 KiB |