Added latest Android Java port
10
ports/android/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
17
ports/android/.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>android</name>
|
||||
<comment>Project android created by Buildship.</comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
2
ports/android/.settings/org.eclipse.buildship.core.prefs
Normal file
@@ -0,0 +1,2 @@
|
||||
connection.project.dir=
|
||||
eclipse.preferences.version=1
|
||||
1
ports/android/app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
42
ports/android/app/build.gradle
Normal file
@@ -0,0 +1,42 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 23
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags ""
|
||||
}
|
||||
}
|
||||
ndk {
|
||||
// Tells Gradle to build outputs for the following ABIs and package
|
||||
// them into your APK.
|
||||
abiFilters 'armeabi-v7a'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "../../../CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0-alpha3'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
}
|
||||
21
ports/android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# 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 *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.example.zerotier;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.example.zerotier", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
25
ports/android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.zerotier">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
194
ports/android/app/src/main/java/com/zerotier/libzt/ZeroTier.java
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* ZeroTier SDK - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* You can be released from the requirements of the license by purchasing
|
||||
* a commercial license. Buying such a license is mandatory as soon as you
|
||||
* develop commercial closed-source software that incorporates or links
|
||||
* directly against ZeroTier software without disclosing the source code
|
||||
* of your own application.
|
||||
*/
|
||||
|
||||
package com.zerotier.libzt;
|
||||
|
||||
import java.net.*;
|
||||
|
||||
public class ZeroTier
|
||||
{
|
||||
static
|
||||
{
|
||||
// loads libzt.so or libzt.dylib
|
||||
System.loadLibrary("zt");
|
||||
init();
|
||||
}
|
||||
|
||||
public static int ZTS_ERR_OK = 0; // Everything is ok
|
||||
public static int ZTS_ERR_INVALID_ARG = -1; // A parameter provided by the user application is invalid (e.g. our of range, NULL, etc)
|
||||
public static int ZTS_ERR_SERVICE = -2; // The service isn't initialized or is for some other reason currently unavailable
|
||||
public static int ZTS_ERR_INVALID_OP = -3; // For some reason this API operation is not permitted (perhaps the service is still starting?)
|
||||
|
||||
// Events generated by ZeroTier service or by libzt
|
||||
// See ext/ZeroTierOne/include/ZeroTierOne.h
|
||||
public static int EVENT_NONE = -1;
|
||||
// Node-specific events
|
||||
public static int EVENT_NODE_UP = 0;
|
||||
public static int EVENT_NODE_OFFLINE = 1;
|
||||
public static int EVENT_NODE_ONLINE = 2;
|
||||
public static int EVENT_NODE_DOWN = 3;
|
||||
public static int EVENT_NODE_IDENTITY_COLLISION = 4;
|
||||
// libzt node events
|
||||
public static int EVENT_NODE_UNRECOVERABLE_ERROR = 16;
|
||||
public static int EVENT_NODE_NORMAL_TERMINATION = 17;
|
||||
// Network-specific events
|
||||
public static int EVENT_NETWORK_NOT_FOUND = 32;
|
||||
public static int EVENT_NETWORK_CLIENT_TOO_OLD = 33;
|
||||
public static int EVENT_NETWORK_REQUESTING_CONFIG = 34;
|
||||
public static int EVENT_NETWORK_OK = 35;
|
||||
public static int EVENT_NETWORK_ACCESS_DENIED = 36;
|
||||
public static int EVENT_NETWORK_READY_IP4 = 37;
|
||||
public static int EVENT_NETWORK_READY_IP6 = 38;
|
||||
public static int EVENT_NETWORK_DOWN = 39;
|
||||
// lwIP netif events
|
||||
public static int EVENT_NETIF_UP_IP4 = 64;
|
||||
public static int EVENT_NETIF_UP_IP6 = 65;
|
||||
public static int EVENT_NETIF_DOWN_IP4 = 66;
|
||||
public static int EVENT_NETIF_DOWN_IP6 = 67;
|
||||
public static int EVENT_NETIF_REMOVED = 68;
|
||||
public static int EVENT_NETIF_LINK_UP = 69;
|
||||
public static int EVENT_NETIF_LINK_DOWN = 70;
|
||||
public static int EVENT_NETIF_NEW_ADDRESS = 71;
|
||||
// Peer events
|
||||
public static int EVENT_PEER_P2P = 96;
|
||||
public static int EVENT_PEER_RELAY = 97;
|
||||
public static int EVENT_PEER_UNREACHABLE = 98;
|
||||
|
||||
// Socket protocol types
|
||||
public static int SOCK_STREAM = 0x00000001;
|
||||
public static int SOCK_DGRAM = 0x00000002;
|
||||
public static int SOCK_RAW = 0x00000003;
|
||||
// Socket family types
|
||||
public static int AF_INET = 0x00000002;
|
||||
public static int AF_INET6 = 0x0000000a;
|
||||
public static int PF_INET = AF_INET;
|
||||
public static int PF_INET6 = AF_INET6;
|
||||
// Used as level numbers for setsockopt() and getsockopt()
|
||||
public static int IPPROTO_IP = 0x00000000;
|
||||
public static int IPPROTO_ICMP = 0x00000001;
|
||||
public static int IPPROTO_TCP = 0x00000006;
|
||||
public static int IPPROTO_UDP = 0x00000011;
|
||||
public static int IPPROTO_IPV6 = 0x00000029;
|
||||
public static int IPPROTO_ICMPV6 = 0x0000003a;
|
||||
public static int IPPROTO_UDPLITE = 0x00000088;
|
||||
public static int IPPROTO_RAW = 0x000000ff;
|
||||
// send() and recv() flags
|
||||
public static int MSG_PEEK = 0x00000001;
|
||||
public static int MSG_WAITALL = 0x00000002;
|
||||
public static int MSG_OOB = 0x00000004;
|
||||
public static int MSG_DONTWAIT = 0x00000008;
|
||||
public static int MSG_MORE = 0x00000010;
|
||||
// fnctl() commands
|
||||
public static int F_GETFL = 0x00000003;
|
||||
public static int F_SETFL = 0x00000004;
|
||||
// fnctl() flags
|
||||
public static int O_NONBLOCK = 0x00000001;
|
||||
public static int O_NDELAY = 0x00000001;
|
||||
// Shutdown commands
|
||||
public static int SHUT_RD = 0x00000000;
|
||||
public static int SHUT_WR = 0x00000001;
|
||||
public static int SHUT_RDWR = 0x00000002;
|
||||
// ioctl() commands
|
||||
public static int FIONREAD = 0x4008667F;
|
||||
public static int FIONBIO = 0x8008667E;
|
||||
// Socket level option number
|
||||
public static int SOL_SOCKET = 0x00000FFF;
|
||||
// Socket options
|
||||
public static int SO_REUSEADDR = 0x00000004;
|
||||
public static int SO_KEEPALIVE = 0x00000008;
|
||||
public static int SO_BROADCAST = 0x00000020;
|
||||
// Socket options
|
||||
public static int SO_DEBUG = 0x00000001; // NOT YET SUPPORTED
|
||||
public static int SO_ACCEPTCONN = 0x00000002;
|
||||
public static int SO_DONTROUTE = 0x00000010; // NOT YET SUPPORTED
|
||||
public static int SO_USELOOPBACK = 0x00000040; // NOT YET SUPPORTED
|
||||
public static int SO_LINGER = 0x00000080;
|
||||
public static int SO_DONTLINGER = ((int)(~SO_LINGER));
|
||||
public static int SO_OOBINLINE = 0x00000100; // NOT YET SUPPORTED
|
||||
public static int SO_REUSEPORT = 0x00000200; // NOT YET SUPPORTED
|
||||
public static int SO_SNDBUF = 0x00001001; // NOT YET SUPPORTED
|
||||
public static int SO_RCVBUF = 0x00001002;
|
||||
public static int SO_SNDLOWAT = 0x00001003; // NOT YET SUPPORTED
|
||||
public static int SO_RCVLOWAT = 0x00001004; // NOT YET SUPPORTED
|
||||
public static int SO_SNDTIMEO = 0x00001005;
|
||||
public static int SO_RCVTIMEO = 0x00001006;
|
||||
public static int SO_ERROR = 0x00001007;
|
||||
public static int SO_TYPE = 0x00001008;
|
||||
public static int SO_CONTIMEO = 0x00001009; // NOT YET SUPPORTED
|
||||
public static int SO_NO_CHECK = 0x0000100a;
|
||||
// IPPROTO_IP options
|
||||
public static int IP_TOS = 0x00000001;
|
||||
public static int IP_TTL = 0x00000002;
|
||||
// IPPROTO_TCP options
|
||||
public static int TCP_NODELAY = 0x00000001;
|
||||
public static int TCP_KEEPALIVE = 0x00000002;
|
||||
public static int TCP_KEEPIDLE = 0x00000003;
|
||||
public static int TCP_KEEPINTVL = 0x00000004;
|
||||
public static int TCP_KEEPCNT = 0x00000005;
|
||||
|
||||
public static native void start(String path, ZeroTierEventListener callbackClass, int port);
|
||||
public static native void stop();
|
||||
public static native int join(long nwid);
|
||||
public static native int leave(long nwid);
|
||||
public static native long get_node_id();
|
||||
public static native int get_num_assigned_addresses(long nwid);
|
||||
public static native void get_6plane_addr(long nwid, long nodeId, ZeroTierSocketAddress addr);
|
||||
public static native void get_rfc4193_addr(long nwid, long nodeId, ZeroTierSocketAddress addr);
|
||||
public static native int get_peer_status(long nodeId);
|
||||
|
||||
public static native int socket(int family, int type, int protocol);
|
||||
public static native int connect(int fd, ZeroTierSocketAddress addr);
|
||||
public static native int bind(int fd, ZeroTierSocketAddress addr);
|
||||
public static native int listen(int fd, int backlog);
|
||||
public static native int accept(int fd, ZeroTierSocketAddress addr);
|
||||
public static native int accept4(int fd, String addr, int port);
|
||||
|
||||
public static native int setsockopt(int fd, int level, int optname, ZeroTierSocketOptionValue optval);
|
||||
public static native int getsockopt(int fd, int level, int optname, ZeroTierSocketOptionValue optval);
|
||||
|
||||
public static native int read(int fd, byte[] buf);
|
||||
public static native int read_offset(int fd, byte[] buf, int offset, int len);
|
||||
public static native int read_length(int fd, byte[] buf, int len);
|
||||
public static native int recv(int fd, byte[] buf, int flags);
|
||||
public static native int recvfrom(int fd, byte[] buf, int flags, ZeroTierSocketAddress addr);
|
||||
|
||||
public static native int write(int fd, byte[] buf);
|
||||
public static native int write_byte(int fd, byte b);
|
||||
public static native int write_offset(int fd, byte[] buf, int offset, int len);
|
||||
public static native int sendto(int fd, byte[] buf, int flags, ZeroTierSocketAddress addr);
|
||||
public static native int send(int fd, byte[] buf, int flags);
|
||||
|
||||
public static native int shutdown(int fd, int how);
|
||||
public static native int close(int fd);
|
||||
|
||||
public static native boolean getsockname(int fd, ZeroTierSocketAddress addr);
|
||||
public static native int getpeername(int fd, ZeroTierSocketAddress addr);
|
||||
public static native int fcntl(int sock, int cmd, int flag);
|
||||
public static native int ioctl(int fd, long request, ZeroTierIoctlArg arg);
|
||||
public static native int select(int nfds, ZeroTierFileDescriptorSet readfds, ZeroTierFileDescriptorSet writefds, ZeroTierFileDescriptorSet exceptfds, int timeout_sec, int timeout_usec);
|
||||
|
||||
public static native void init(); // Only to be called by static initializer of this class
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
public interface ZeroTierEventListener {
|
||||
|
||||
/*
|
||||
* Called when an even occurs in the native section of the ZeroTier library service
|
||||
*/
|
||||
public void onZeroTierEvent(long nwid, int eventCode);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
public class ZeroTierFileDescriptorSet
|
||||
{
|
||||
byte[] fds_bits = new byte[1024];
|
||||
|
||||
public void CLR(int fd)
|
||||
{
|
||||
fds_bits[fd] = 0x00;
|
||||
}
|
||||
|
||||
public boolean ISSET(int fd)
|
||||
{
|
||||
return fds_bits[fd] == 0x01;
|
||||
}
|
||||
|
||||
public void SET(int fd)
|
||||
{
|
||||
fds_bits[fd] = 0x01;
|
||||
}
|
||||
|
||||
public void ZERO()
|
||||
{
|
||||
for (int i=0; i<1024; i++) {
|
||||
fds_bits[i] = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
import com.zerotier.libzt.ZeroTier;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ZeroTierInputStream extends InputStream
|
||||
{
|
||||
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
|
||||
private static final int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
/*
|
||||
* File descriptor used by lower native layer
|
||||
*/
|
||||
int zfd;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public int available()
|
||||
throws IOException
|
||||
{
|
||||
return 0; // NOT YET SUPPORTED
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public void close()
|
||||
throws IOException
|
||||
{
|
||||
/* Note: this operation currently only stops RX on a socket that is shared
|
||||
between both I/OStreams. This means that closing this stream will only shutdown
|
||||
that aspect of the socket but not actually close it and free resources. Resources
|
||||
will be properly freed when the socket implementation's close() is called or if
|
||||
both I/OStreams are closed separately */
|
||||
ZeroTier.shutdown(zfd, ZeroTier.SHUT_RD);
|
||||
zfd = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public void mark(int readlimit)
|
||||
{
|
||||
System.err.println("mark: ZeroTierInputStream does not currently support this feature");
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public void reset()
|
||||
throws IOException
|
||||
{
|
||||
System.err.println("reset: ZeroTierInputStream does not currently support this feature");
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public boolean markSupported()
|
||||
{
|
||||
return false; // mark() is not supported
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public long transferTo(OutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
Objects.requireNonNull(out, "out must not be null");
|
||||
int bytesTransferred = 0, bytesRead;
|
||||
byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
|
||||
while (((bytesRead = ZeroTier.read(zfd, buf)) >= 0)) {
|
||||
out.write(buf, 0, bytesRead);
|
||||
bytesTransferred += bytesRead;
|
||||
}
|
||||
return bytesTransferred;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public int read()
|
||||
throws IOException
|
||||
{
|
||||
byte[] buf = new byte[1];
|
||||
// Unlike a native read(), if nothing is read we should return -1
|
||||
int retval = ZeroTier.read(zfd, buf);
|
||||
if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) {
|
||||
return -1;
|
||||
}
|
||||
if (retval < 0) {
|
||||
throw new IOException("read(), errno="+retval);
|
||||
}
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public int read(byte[] b)
|
||||
throws IOException
|
||||
{
|
||||
Objects.requireNonNull(b, "input byte array must not be null");
|
||||
// Unlike a native read(), if nothing is read we should return -1
|
||||
int retval = ZeroTier.read(zfd, b);
|
||||
if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) {
|
||||
return -1;
|
||||
}
|
||||
if (retval < 0) {
|
||||
throw new IOException("read(b), errno="+retval);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public int read(byte[] b, int off, int len)
|
||||
throws IOException
|
||||
{
|
||||
Objects.requireNonNull(b, "input byte array must not be null");
|
||||
if ((off < 0) | (len < 0) | (len > b.length - off)) {
|
||||
throw new IndexOutOfBoundsException("invalid argument");
|
||||
}
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
// Unlike a native read(), if nothing is read we should return -1
|
||||
int retval = ZeroTier.read_offset(zfd, b, off, len);
|
||||
if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) {
|
||||
return -1;
|
||||
}
|
||||
if (retval < 0) {
|
||||
throw new IOException("read(b,off,len), errno="+retval);
|
||||
}
|
||||
//System.out.println("readNBytes(byte[] b, int off="+off+", int len="+len+")="+retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public byte[] readAllBytes()
|
||||
throws IOException
|
||||
{
|
||||
//System.out.println("readAllBytes()");
|
||||
ZeroTierIoctlArg ztarg = new ZeroTierIoctlArg();
|
||||
int err = ZeroTier.ioctl(zfd, ZeroTier.FIONREAD, ztarg);
|
||||
byte[] buf = new byte[ztarg.integer];
|
||||
int retval = ZeroTier.read(zfd, buf);
|
||||
if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) {
|
||||
// No action needed
|
||||
}
|
||||
if (retval < 0) {
|
||||
throw new IOException("readAllBytes(b,off,len), errno="+retval);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public int readNBytes(byte[] b, int off, int len)
|
||||
throws IOException
|
||||
{
|
||||
Objects.requireNonNull(b, "input byte array must not be null");
|
||||
if ((off < 0) | (len < 0) | (len > b.length - off)) {
|
||||
throw new IndexOutOfBoundsException("invalid argument");
|
||||
}
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
int retval = ZeroTier.read_offset(zfd, b, off, len);
|
||||
if ((retval == 0) | (retval == -104) /* EINTR, from SO_RCVTIMEO */) {
|
||||
// No action needed
|
||||
}
|
||||
if (retval < 0) {
|
||||
throw new IOException("readNBytes(b,off,len), errno="+retval);
|
||||
}
|
||||
//System.out.println("readNBytes(byte[] b, int off="+off+", int len="+len+")="+retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public long skip(long n)
|
||||
throws IOException
|
||||
{
|
||||
//System.out.println("skip()");
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
long bytesRemaining = n, bytesRead;
|
||||
int bufSize = (int)Math.min(MAX_SKIP_BUFFER_SIZE, bytesRemaining);
|
||||
byte[] buf = new byte[bufSize];
|
||||
while (bytesRemaining > 0) {
|
||||
if ((bytesRead = ZeroTier.read_length(zfd, buf, (int)Math.min(bufSize, bytesRemaining))) < 0) {
|
||||
break;
|
||||
}
|
||||
bytesRemaining -= bytesRead;
|
||||
}
|
||||
return n - bytesRemaining; // skipped
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
public class ZeroTierIoctlArg
|
||||
{
|
||||
int integer; // General integer to be used or updated by the zts_ioctl() call
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
import com.zerotier.libzt.ZeroTier;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
class ZeroTierOutputStream extends OutputStream
|
||||
{
|
||||
/*
|
||||
* File descriptor used by lower native layer
|
||||
*/
|
||||
int zfd;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public void flush()
|
||||
throws IOException
|
||||
{
|
||||
// System.err.println("flush: ZeroTierOutputStream does not currently support this feature");
|
||||
// Not fully supported since we don't maintain a buffer
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public void close()
|
||||
throws IOException
|
||||
{
|
||||
/* Note: this operation currently only stops RX on a socket that is shared
|
||||
between both I/OStreams. This means that closing this stream will only shutdown
|
||||
that aspect of the socket but not actually close it and free resources. Resources
|
||||
will be properly freed when the socket implementation's close() is called or if
|
||||
both I/OStreams are closed separately */
|
||||
ZeroTier.shutdown(zfd, ZeroTier.SHUT_WR);
|
||||
zfd = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public void write(byte[] b)
|
||||
throws IOException
|
||||
{
|
||||
int err = ZeroTier.write(zfd, b);
|
||||
if (err < 0) {
|
||||
throw new IOException("write(b[]), errno="+err);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public void write(byte[] b, int off, int len)
|
||||
throws IOException
|
||||
{
|
||||
Objects.requireNonNull(b, "input byte array must not be null");
|
||||
if ((off < 0) | (len < 0) | (off+len > b.length)) {
|
||||
throw new IndexOutOfBoundsException("write(b,off,len)");
|
||||
}
|
||||
int err = ZeroTier.write_offset(zfd, b, off, len);
|
||||
if (err < 0) {
|
||||
throw new IOException("write(b[],off,len), errno="+err);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public void write(int b)
|
||||
throws IOException
|
||||
{
|
||||
byte lowByte = (byte)(b & 0xFF);
|
||||
int err = ZeroTier.write_byte(zfd, lowByte);
|
||||
if (err < 0) {
|
||||
throw new IOException("write(b), errno="+err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
import com.zerotier.libzt.ZeroTierSocket;
|
||||
|
||||
import java.net.*;
|
||||
import javax.net.SocketFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.*;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
public class ZeroTierSSLSocketFactory extends SSLSocketFactory
|
||||
{
|
||||
private final SSLSocketFactory delegate;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public ZeroTierSSLSocketFactory(SSLSocketFactory delegate)
|
||||
{
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
|
||||
throws IOException
|
||||
{
|
||||
ZeroTierSocket zs = new ZeroTierSocket();
|
||||
zs.connect((SocketAddress)new InetSocketAddress(host, port), 10);
|
||||
return delegate.createSocket(zs, host, port, autoClose);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public Socket createSocket(Socket s, InputStream consumed, boolean autoClose)
|
||||
throws IOException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public Socket createSocket(InetAddress a,int b,InetAddress c,int d)
|
||||
throws IOException
|
||||
{
|
||||
ZeroTierSocket s = new ZeroTierSocket();
|
||||
return delegate.createSocket(a, b, c, d);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public Socket createSocket(InetAddress a,int b)
|
||||
throws IOException
|
||||
{
|
||||
ZeroTierSocket s = new ZeroTierSocket();
|
||||
return delegate.createSocket(a, b);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public Socket createSocket(String a,int b,InetAddress c,int d)
|
||||
throws IOException
|
||||
{
|
||||
ZeroTierSocket s = new ZeroTierSocket();
|
||||
return delegate.createSocket(a, b, c, d);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public Socket createSocket(String a,int b)
|
||||
throws IOException
|
||||
{
|
||||
ZeroTierSocket s = new ZeroTierSocket();
|
||||
return delegate.createSocket(a, b);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public String [] getSupportedCipherSuites()
|
||||
{
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public String [] getDefaultCipherSuites()
|
||||
{
|
||||
return new String[0];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,746 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
import com.zerotier.libzt.ZeroTier;
|
||||
import com.zerotier.libzt.ZeroTierSocketAddress;
|
||||
import com.zerotier.libzt.ZeroTierSocketImpl;
|
||||
import com.zerotier.libzt.ZeroTierSocketImplFactory;
|
||||
import com.zerotier.libzt.ZeroTierInputStream;
|
||||
import com.zerotier.libzt.ZeroTierOutputStream;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.Objects;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.net.InetAddress;
|
||||
|
||||
public class ZeroTierSocket extends Socket
|
||||
{
|
||||
/*
|
||||
* Factory designated to create the underlying ZeroTierSocket implementation
|
||||
*/
|
||||
static ZeroTierSocketImplFactory factory = new ZeroTierSocketImplFactory();
|
||||
|
||||
/*
|
||||
* Underlying implementation of this ZeroTierSocket
|
||||
*/
|
||||
ZeroTierSocketImpl impl;
|
||||
|
||||
/*
|
||||
* Misc. state flags
|
||||
*/
|
||||
private boolean created = false;
|
||||
private boolean closed = false;
|
||||
private boolean connected = false;
|
||||
private boolean bound = false;
|
||||
private boolean inputShutdown = false;
|
||||
private boolean outputShutdown = false;
|
||||
|
||||
/*
|
||||
* Creates and sets the implementation
|
||||
*/
|
||||
void setImpl()
|
||||
{
|
||||
if (factory != null) {
|
||||
impl = factory.createSocketImpl();
|
||||
}
|
||||
if (impl != null) {
|
||||
impl.setSocket(this);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the underlying socket implementation
|
||||
*/
|
||||
private ZeroTierSocketImpl getImpl()
|
||||
throws SocketException
|
||||
{
|
||||
if (!created) {
|
||||
try {
|
||||
impl.create(true);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw (SocketException) new SocketException().initCause(ex);
|
||||
}
|
||||
created = true;
|
||||
}
|
||||
return impl;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the underlying socket implementation
|
||||
*/
|
||||
void createImpl(boolean stream)
|
||||
throws SocketException
|
||||
{
|
||||
if (impl == null) {
|
||||
setImpl();
|
||||
}
|
||||
try {
|
||||
impl.create(stream);
|
||||
created = true;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new SocketException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor for ZeroTierSocket
|
||||
*/
|
||||
public ZeroTierSocket()
|
||||
throws IOException
|
||||
{
|
||||
this((InetAddress)null, 0, null, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates an unconnected socket
|
||||
*/
|
||||
protected ZeroTierSocket(ZeroTierSocketImpl impl)
|
||||
throws SocketException
|
||||
{
|
||||
this.impl = impl;
|
||||
if (impl != null) {
|
||||
this.impl.setSocket(this);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor for ZeroTierSocket
|
||||
*/
|
||||
public ZeroTierSocket(InetAddress raddr, int rport, InetAddress laddr, int lport)
|
||||
throws IOException
|
||||
{
|
||||
setImpl();
|
||||
|
||||
try {
|
||||
if (laddr != null) {
|
||||
bind(new InetSocketAddress(laddr, lport));
|
||||
}
|
||||
if (raddr != null) {
|
||||
connect(new InetSocketAddress(raddr, rport));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try {
|
||||
close();
|
||||
}
|
||||
catch (IOException _ex) {
|
||||
ex.addSuppressed(_ex);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor for ZeroTierSocket
|
||||
*/
|
||||
public ZeroTierSocket(InetAddress address, int port)
|
||||
throws IOException
|
||||
{
|
||||
this(address, port, null, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor for ZeroTierSocket
|
||||
*/
|
||||
public ZeroTierSocket(String address, int port)
|
||||
throws IOException
|
||||
{
|
||||
this(InetAddress.getByName(address), port, null, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor for ZeroTierSocket
|
||||
*/
|
||||
public ZeroTierSocket(String address, int port, InetAddress localHost, int localPort)
|
||||
throws IOException
|
||||
{
|
||||
this(InetAddress.getByName(address), port, localHost, localPort);
|
||||
}
|
||||
|
||||
/*
|
||||
* Binds the socket to a local address
|
||||
*/
|
||||
public void bind(SocketAddress localAddr)
|
||||
throws IOException
|
||||
{
|
||||
if (isSocketBound()) {
|
||||
throw new SocketException("bind: ZeroTierSocket is already bound");
|
||||
}
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("bind: ZeroTierSocket is closed");
|
||||
}
|
||||
if (localAddr != null && (!(localAddr instanceof InetSocketAddress))) {
|
||||
throw new IllegalArgumentException("bind: Unsupported address type");
|
||||
}
|
||||
InetSocketAddress addr = (InetSocketAddress)localAddr;
|
||||
if (addr != null && addr.isUnresolved()) {
|
||||
throw new SocketException("bind: Unresolved address");
|
||||
}
|
||||
if (addr == null) {
|
||||
addr = new InetSocketAddress(0);
|
||||
}
|
||||
getImpl().bind(addr.getAddress(), addr.getPort());
|
||||
bound = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes the socket
|
||||
*/
|
||||
public synchronized void close()
|
||||
throws IOException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
return;
|
||||
}
|
||||
getOutputStream().flush();
|
||||
impl.close();
|
||||
closed = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Connects the socket to a remote address
|
||||
*/
|
||||
public void connect(SocketAddress remoteAddr)
|
||||
throws IOException
|
||||
{
|
||||
connect(remoteAddr, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Connects the socket to a remote address
|
||||
*/
|
||||
public void connect(SocketAddress remoteAddr, int timeout)
|
||||
throws IOException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("connect: ZeroTierSocket is closed");
|
||||
}
|
||||
if (isSocketConnected()) {
|
||||
throw new SocketException("connect: already connected");
|
||||
}
|
||||
if (remoteAddr == null) {
|
||||
throw new IllegalArgumentException("connect: The address can't be null");
|
||||
}
|
||||
if (!(remoteAddr instanceof InetSocketAddress)) {
|
||||
throw new IllegalArgumentException("connect: Unsupported address type");
|
||||
}
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("connect: timeout cannot be negative");
|
||||
}
|
||||
if (!created) {
|
||||
createImpl(true);
|
||||
}
|
||||
getImpl().connect(remoteAddr, 0);
|
||||
bound = true;
|
||||
connected = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the associated Channel
|
||||
*/
|
||||
public SocketChannel getChannel()
|
||||
{
|
||||
System.err.println("getChannel: ZeroTierSocket does not currently support this feature");
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the address to which the socket is connected
|
||||
*/
|
||||
public InetAddress getInetAddress()
|
||||
{
|
||||
if (!isSocketConnected()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getImpl().getInetAddress();
|
||||
}
|
||||
catch (SocketException ex) {
|
||||
// Not Reachable
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the input stream
|
||||
*/
|
||||
public ZeroTierInputStream getInputStream()
|
||||
throws IOException
|
||||
{
|
||||
if (!isSocketConnected()) {
|
||||
throw new SocketException("ZeroTierSocket is not connected");
|
||||
}
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
if (isInputStreamShutdown()) {
|
||||
throw new SocketException("ZeroTierSocket input is shutdown");
|
||||
}
|
||||
return getImpl().getInputStream();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether SO_KEEPALIVE is enabled
|
||||
*/
|
||||
public boolean getKeepAlive()
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
return ((Boolean) getImpl().getOption(ZeroTier.SO_KEEPALIVE)).booleanValue();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the local address to which the socket is bound
|
||||
*/
|
||||
public InetAddress getLocalAddress()
|
||||
{
|
||||
System.err.println("getLocalAddress: ZeroTierSocket does not currently support this feature");
|
||||
/*
|
||||
// This is for backward compatibility
|
||||
if (!isSocketBound()) {
|
||||
return InetAddress.anyLocalAddress();
|
||||
}
|
||||
InetAddress inAddr = null;
|
||||
try {
|
||||
inAddr = (InetAddress) getImpl().getOption(ZeroTier.SO_BINDADDR);
|
||||
if (inAddr.isAnyLocalAddress()) {
|
||||
inAddr = InetAddress.anyLocalAddress();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// "0.0.0.0"
|
||||
inAddr = InetAddress.anyLocalAddress();
|
||||
}
|
||||
return inAddr;
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the local port to which the socket is bound
|
||||
*/
|
||||
public int getLocalPort()
|
||||
{
|
||||
if (!isSocketBound()) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
return getImpl().getLocalPort();
|
||||
}
|
||||
catch(SocketException ex) {
|
||||
// Unreachable
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the address of the endpoint that the socket is bound to.
|
||||
*/
|
||||
public SocketAddress getLocalSocketAddress()
|
||||
{
|
||||
if (!isSocketBound()) {
|
||||
return null;
|
||||
}
|
||||
return new InetSocketAddress(getLocalAddress(), getLocalPort());
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether SO_OOBINLINE is enabled.
|
||||
*/
|
||||
public boolean getOOBInline()
|
||||
throws SocketException
|
||||
{
|
||||
System.err.println("getOOBInline: ZeroTierSocket does not currently support this feature");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the output stream.
|
||||
*/
|
||||
public ZeroTierOutputStream getOutputStream()
|
||||
throws IOException
|
||||
{
|
||||
if (!isSocketConnected()) {
|
||||
throw new SocketException("ZeroTierSocket is not connected");
|
||||
}
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
if (isOutputStreamShutdown()) {
|
||||
throw new SocketException("ZeroTierSocket output is shutdown");
|
||||
}
|
||||
return getImpl().getOutputStream();
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the remote port to which the socket is connected
|
||||
*/
|
||||
public int getPort()
|
||||
{
|
||||
if (!isSocketConnected()) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
return getImpl().getPort();
|
||||
}
|
||||
catch (SocketException ex) {
|
||||
// Not reachable
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns SO_RCVBUF
|
||||
*/
|
||||
public synchronized int getReceiveBufferSize()
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
Object opt = getImpl().getOption(ZeroTier.SO_RCVBUF);
|
||||
int sz = 0;
|
||||
if (opt instanceof Integer) {
|
||||
sz = ((Integer)opt).intValue();
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the remote address to which this socket is connected
|
||||
*/
|
||||
public SocketAddress getRemoteSocketAddress()
|
||||
{
|
||||
if (!isSocketConnected()) {
|
||||
return null;
|
||||
}
|
||||
return new InetSocketAddress(getInetAddress(), getPort());
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks whether SO_REUSEADDR is enabled.
|
||||
*/
|
||||
public boolean getReuseAddress()
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
return ((Boolean)(getImpl().getOption(ZeroTier.SO_REUSEADDR))).booleanValue();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns SO_SNDBUF.
|
||||
*/
|
||||
public synchronized int getSendBufferSize()
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
int sz = 0;
|
||||
Object opt = getImpl().getOption(ZeroTier.SO_SNDBUF);
|
||||
if (opt instanceof Integer) {
|
||||
sz = ((Integer)opt).intValue();
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns SO_LINGER.
|
||||
*/
|
||||
public int getSoLinger()
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
Object opt = getImpl().getOption(ZeroTier.SO_LINGER);
|
||||
if (opt instanceof Integer) {
|
||||
return ((Integer)opt).intValue();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns SO_TIMEOUT.
|
||||
*/
|
||||
public synchronized int getSoTimeout()
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
Object opt = getImpl().getOption(ZeroTier.SO_RCVTIMEO);
|
||||
if (opt instanceof Integer) {
|
||||
return ((Integer)opt).intValue();
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks whether TCP_NODELAY is enabled.
|
||||
*/
|
||||
public boolean getTcpNoDelay()
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
return ((Boolean)getImpl().getOption(ZeroTier.TCP_NODELAY)).booleanValue();
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets traffic class or type-of-service in the IP header for packets sent from this Socket
|
||||
*/
|
||||
public int getTrafficClass()
|
||||
throws SocketException
|
||||
{
|
||||
System.err.println("getTrafficClass: ZeroTierSocket does not currently support this feature");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether or not the socket is bound to a local interface.
|
||||
*/
|
||||
public boolean isSocketBound()
|
||||
{
|
||||
return bound;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether or not the socket has been closed.
|
||||
*/
|
||||
public boolean isSocketClosed()()
|
||||
{
|
||||
return closed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether or not the socket is connected to a remote host.
|
||||
*/
|
||||
public boolean isSocketConnected()
|
||||
{
|
||||
return connected;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the input aspect of the socket has been disabled.
|
||||
*/
|
||||
public boolean isInputStreamShutdown()
|
||||
{
|
||||
return inputShutdown;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the output aspect of the socket has been disabled.
|
||||
*/
|
||||
public boolean isOutputStreamShutdown()
|
||||
{
|
||||
return outputShutdown;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a byte of urgent data on the socket.
|
||||
*/
|
||||
public void sendUrgentData(int data)
|
||||
throws IOException
|
||||
{
|
||||
System.err.println("sendUrgentData: ZeroTierSocket does not currently support this feature");
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable SO_KEEPALIVE.
|
||||
*/
|
||||
public void setKeepAlive(boolean on)
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
getImpl().setOption(ZeroTier.SO_KEEPALIVE, Boolean.valueOf(on));
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable SO_OOBINLINE.
|
||||
*/
|
||||
public void setOOBInline(boolean on)
|
||||
throws SocketException
|
||||
{
|
||||
System.err.println("setOOBInline: ZeroTierSocket does not currently support this feature");
|
||||
}
|
||||
|
||||
/*
|
||||
* Set performance preferences.
|
||||
*/
|
||||
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
|
||||
{
|
||||
System.err.println("setPerformancePreferences: ZeroTierSocket does not currently support this feature");
|
||||
}
|
||||
|
||||
/*
|
||||
* Set SO_RCVBUF.
|
||||
*/
|
||||
public synchronized void setReceiveBufferSize(int size)
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
if (size <= 0) {
|
||||
throw new IllegalArgumentException("invalid receive buffer size argument");
|
||||
}
|
||||
getImpl().setOption(ZeroTier.SO_RCVBUF, new Integer(size));
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable SO_REUSEADDR.
|
||||
*/
|
||||
public void setReuseAddress(boolean on)
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
getImpl().setOption(ZeroTier.SO_REUSEADDR, Boolean.valueOf(on));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set SO_SNDBUF.
|
||||
*/
|
||||
public synchronized void setSendBufferSize(int size)
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
if (size < 0) {
|
||||
throw new IllegalArgumentException("size argument cannot be negative");
|
||||
}
|
||||
getImpl().setOption(ZeroTier.SO_SNDBUF, new Integer(size));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set Socket implementation factory for all clients.
|
||||
*/
|
||||
public static void setSocketImplFactory(ZeroTierSocketImplFactory fact)
|
||||
throws IOException
|
||||
{
|
||||
if (factory != null) {
|
||||
throw new SocketException("ZeroTierSocket factory is already defined");
|
||||
}
|
||||
factory = fact;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable SO_LINGER time (seconds).
|
||||
*/
|
||||
public void setSoLinger(boolean on, int linger)
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
if (!on) {
|
||||
getImpl().setOption(ZeroTier.SO_LINGER, new Boolean(on));
|
||||
}
|
||||
else {
|
||||
if (linger < 0) {
|
||||
throw new IllegalArgumentException("linger argument is invalid");
|
||||
}
|
||||
if (linger > 0xFFFF) {
|
||||
linger = 0xFFFF;
|
||||
}
|
||||
getImpl().setOption(ZeroTier.SO_LINGER, new Integer(linger));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable SO_TIMEOUT with the specified timeout, in milliseconds.
|
||||
*/
|
||||
public void setSoTimeout(int timeout)
|
||||
throws SocketException
|
||||
{
|
||||
if (timeout < 0) {
|
||||
throw new IllegalArgumentException("timeout argument cannot be negative");
|
||||
}
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
getImpl().setOption(ZeroTier.SO_RCVTIMEO, new Integer(timeout));
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable or disable TCP_NODELAY (Nagle's algorithm).
|
||||
*/
|
||||
public void setTcpNoDelay(boolean on)
|
||||
throws SocketException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
getImpl().setOption(ZeroTier.TCP_NODELAY, Boolean.valueOf(on));
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets traffic class or ToS.
|
||||
*/
|
||||
public void setTrafficClass(int tc)
|
||||
throws SocketException
|
||||
{
|
||||
System.err.println("setTrafficClass: ZeroTierSocket does not currently support this feature");
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the input stream for this socket.
|
||||
*/
|
||||
public void shutdownInput()
|
||||
throws IOException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
if (isInputStreamShutdown()) {
|
||||
throw new SocketException("ZeroTierSocket input is already shutdown");
|
||||
}
|
||||
if (!isSocketConnected()) {
|
||||
throw new SocketException("ZeroTierSocket is not connected");
|
||||
}
|
||||
getImpl().shutdownInput();
|
||||
inputShutdown = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the output stream for this socket.
|
||||
*/
|
||||
public void shutdownOutput()
|
||||
throws IOException
|
||||
{
|
||||
if (isSocketClosed()) {
|
||||
throw new SocketException("ZeroTierSocket is closed");
|
||||
}
|
||||
if (isOutputStreamShutdown()) {
|
||||
throw new SocketException("ZeroTierSocket output is already shutdown");
|
||||
}
|
||||
if (!isSocketConnected()) {
|
||||
throw new SocketException("ZeroTierSocket is not connected");
|
||||
}
|
||||
getImpl().shutdownOutput();
|
||||
outputShutdown = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the underlying implementation's file descriptor.
|
||||
*/
|
||||
public FileDescriptor getFileDescriptor()
|
||||
{
|
||||
return impl.getFileDescriptor();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
import com.zerotier.libzt.ZeroTier;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
// Designed to transport address information across the JNI boundary
|
||||
public class ZeroTierSocketAddress
|
||||
{
|
||||
public byte[] _ip6 = new byte[16];
|
||||
public byte[] _ip4 = new byte[4];
|
||||
|
||||
public int _family;
|
||||
public int _port; // Also reused for netmask or prefix
|
||||
|
||||
public ZeroTierSocketAddress() {}
|
||||
|
||||
public ZeroTierSocketAddress(String ipStr, int port)
|
||||
{
|
||||
if(ipStr.contains(":")) {
|
||||
_family = ZeroTier.AF_INET6;
|
||||
try {
|
||||
InetAddress ip = InetAddress.getByName(ipStr);
|
||||
_ip6 = ip.getAddress();
|
||||
}
|
||||
catch (Exception e) { }
|
||||
}
|
||||
else if(ipStr.contains(".")) {
|
||||
_family = ZeroTier.AF_INET;
|
||||
try {
|
||||
InetAddress ip = InetAddress.getByName(ipStr);
|
||||
_ip4 = ip.getAddress();
|
||||
}
|
||||
catch (Exception e) { }
|
||||
}
|
||||
_port = port;
|
||||
}
|
||||
|
||||
public int getPort() { return _port; }
|
||||
public int getNetmask() { return _port; }
|
||||
public int getPrefix() { return _port; }
|
||||
|
||||
public String ipString()
|
||||
{
|
||||
if (_family == ZeroTier.AF_INET) {
|
||||
try {
|
||||
InetAddress inet = InetAddress.getByAddress(_ip4);
|
||||
return "" + inet.getHostAddress();
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
if (_family == ZeroTier.AF_INET6) {
|
||||
try {
|
||||
InetAddress inet = InetAddress.getByAddress(_ip6);
|
||||
return "" + inet.getHostAddress();
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public String toString() { return ipString() + ":" + _port; }
|
||||
public String toCIDR() { return ipString() + "/" + _port; }
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
import com.zerotier.libzt.ZeroTier;
|
||||
import com.zerotier.libzt.ZeroTierSocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import javax.net.SocketFactory;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ZeroTierSocketFactory extends SocketFactory
|
||||
{
|
||||
public ZeroTierSocketFactory() { }
|
||||
|
||||
public static SocketFactory getDefault()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Socket createSocket()
|
||||
throws IOException, UnknownHostException
|
||||
{
|
||||
return new ZeroTierSocket();
|
||||
}
|
||||
|
||||
public Socket createSocket(String host, int port)
|
||||
throws IOException, UnknownHostException
|
||||
{
|
||||
return new ZeroTierSocket(host, port);
|
||||
}
|
||||
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
|
||||
throws IOException, UnknownHostException
|
||||
{
|
||||
return new ZeroTierSocket(host, port, localHost, localPort);
|
||||
}
|
||||
|
||||
public Socket createSocket(InetAddress host, int port)
|
||||
throws IOException
|
||||
{
|
||||
return new ZeroTierSocket(host, port);
|
||||
}
|
||||
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
|
||||
throws IOException
|
||||
{
|
||||
return new ZeroTierSocket(address, port, localAddress, localPort);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,772 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
import com.zerotier.libzt.ZeroTier;
|
||||
import com.zerotier.libzt.ZeroTierSocketAddress;
|
||||
import com.zerotier.libzt.ZeroTierInputStream;
|
||||
import com.zerotier.libzt.ZeroTierOutputStream;
|
||||
import com.zerotier.libzt.ZeroTierSocketOptionValue;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.Objects;
|
||||
import java.lang.Object;
|
||||
import java.net.SocketImpl;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Set;
|
||||
import java.lang.Boolean;
|
||||
|
||||
import com.zerotier.libzt.ZeroTier;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
public class ZeroTierSocketImpl extends SocketImpl
|
||||
{
|
||||
private int defaultProtocol = 0;
|
||||
|
||||
/*
|
||||
* File descriptor from lower native layer
|
||||
*/
|
||||
private int zfd = -1;
|
||||
private int zfd4 = -1;
|
||||
private int zfd6 = -1;
|
||||
|
||||
/*
|
||||
* Input and Output streams
|
||||
*/
|
||||
private ZeroTierInputStream in = new ZeroTierInputStream();
|
||||
private ZeroTierOutputStream out = new ZeroTierOutputStream();
|
||||
|
||||
Socket socket = null;
|
||||
ServerSocket serverSocket = null;
|
||||
|
||||
/*
|
||||
* The remote address the socket is connected to
|
||||
*/
|
||||
protected InetAddress address;
|
||||
|
||||
/*
|
||||
* Sets the underlying file descriptor valud for the SocketImpl as well as the Input/OutputStream
|
||||
*/
|
||||
private void setNativeFileDescriptor(int fd)
|
||||
{
|
||||
zfd = fd;
|
||||
in.zfd = fd;
|
||||
out.zfd = fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Various socket options that are cached from calls to setOption() before
|
||||
* the socket exists.
|
||||
*/
|
||||
private int _so_rcvtimeo;
|
||||
private boolean _so_keepalive;
|
||||
private int _so_sndbuf;
|
||||
private int _so_rcvbuf;
|
||||
private boolean _so_reuseaddr;
|
||||
private int _so_linger;
|
||||
private int _so_tos;
|
||||
private boolean _so_nodelay;
|
||||
|
||||
private void setCachedSocketOptions(int fd)
|
||||
{
|
||||
if (fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we previously received a setSoTimeout() call but were unable to process it due
|
||||
// to the fact that the underlying socket didn't even exist yet, do so now.
|
||||
int err = 0;
|
||||
ZeroTierSocketOptionValue optval = new ZeroTierSocketOptionValue();
|
||||
|
||||
try {
|
||||
// SO_TIMEOUT
|
||||
if (_so_rcvtimeo > 0) {
|
||||
optval.isInteger = true;
|
||||
optval.isBoolean = false;
|
||||
optval.integerValue = ((Integer)_so_rcvtimeo).intValue();
|
||||
if ((err = ZeroTier.setsockopt(fd, ZeroTier.SOL_SOCKET, ZeroTier.SO_RCVTIMEO, optval)) < 0) {
|
||||
throw new IOException("socket("+fd+"), errno="+err+", unable to set previously cached SO_RCVTIMEO");
|
||||
}
|
||||
}
|
||||
// SO_KEEPALIVE
|
||||
if (_so_keepalive == true) {
|
||||
optval.isInteger = false;
|
||||
optval.isBoolean = true;
|
||||
optval.booleanValue = ((Boolean)_so_keepalive).booleanValue() ? true : false;
|
||||
if ((err = ZeroTier.setsockopt(fd, ZeroTier.SOL_SOCKET, ZeroTier.SO_KEEPALIVE, optval)) < 0) {
|
||||
throw new IOException("socket("+fd+"), errno="+err+", unable to set previously cached SO_KEEPALIVE");
|
||||
}
|
||||
}
|
||||
// SO_SNDBUF
|
||||
if (_so_sndbuf > 0) {
|
||||
optval.isInteger = true;
|
||||
optval.isBoolean = false;
|
||||
optval.integerValue = ((Integer)_so_sndbuf).intValue();
|
||||
if ((err = ZeroTier.setsockopt(fd, ZeroTier.SOL_SOCKET, ZeroTier.SO_SNDBUF, optval)) < 0) {
|
||||
throw new IOException("socket("+fd+"), errno="+err+", unable to set previously cached SO_SNDBUF");
|
||||
}
|
||||
}
|
||||
// SO_RCVBUF
|
||||
if (_so_rcvbuf > 0) {
|
||||
optval.isInteger = true;
|
||||
optval.isBoolean = false;
|
||||
optval.integerValue = ((Integer)_so_rcvbuf).intValue();
|
||||
if ((err = ZeroTier.setsockopt(fd, ZeroTier.SOL_SOCKET, ZeroTier.SO_RCVBUF, optval)) < 0) {
|
||||
throw new IOException("socket("+fd+"), errno="+err+", unable to set previously cached SO_RCVBUF");
|
||||
}
|
||||
}
|
||||
// SO_REUSEADDR
|
||||
if (_so_reuseaddr == true) {
|
||||
optval.isInteger = false;
|
||||
optval.isBoolean = true;
|
||||
optval.booleanValue = ((Boolean)_so_reuseaddr).booleanValue() ? true : false;
|
||||
if ((err = ZeroTier.setsockopt(fd, ZeroTier.SOL_SOCKET, ZeroTier.SO_REUSEADDR, optval)) < 0) {
|
||||
throw new IOException("socket("+fd+"), errno="+err+", unable to set previously cached SO_REUSEADDR");
|
||||
}
|
||||
}
|
||||
// SO_LINGER
|
||||
if (_so_linger > 0) {
|
||||
optval.isInteger = true;
|
||||
optval.isBoolean = false;
|
||||
optval.integerValue = ((Integer)_so_linger).intValue();
|
||||
if ((err = ZeroTier.setsockopt(fd, ZeroTier.SOL_SOCKET, ZeroTier.SO_LINGER, optval)) < 0) {
|
||||
throw new IOException("socket("+fd+"), errno="+err+", unable to set previously cached SO_LINGER");
|
||||
}
|
||||
}
|
||||
// IP_TOS
|
||||
if (_so_tos > 0) {
|
||||
optval.isInteger = true;
|
||||
optval.isBoolean = false;
|
||||
optval.integerValue = ((Integer)_so_tos).intValue();
|
||||
if ((err = ZeroTier.setsockopt(fd, ZeroTier.SOL_SOCKET, ZeroTier.IP_TOS, optval)) < 0) {
|
||||
throw new IOException("socket("+fd+"), errno="+err+", unable to set previously cached IP_TOS");
|
||||
}
|
||||
}
|
||||
// TCP_NODELAY
|
||||
if (_so_nodelay == true) {
|
||||
optval.isInteger = false;
|
||||
optval.isBoolean = true;
|
||||
optval.booleanValue = ((Boolean)_so_nodelay).booleanValue() ? true : false;
|
||||
if ((err = ZeroTier.setsockopt(fd, ZeroTier.SOL_SOCKET, ZeroTier.TCP_NODELAY, optval)) < 0) {
|
||||
throw new IOException("socket("+fd+"), errno="+err+", unable to set previously cached TCP_NODELAY");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor which creates a new ZeroTierSocketImpl
|
||||
*/
|
||||
public ZeroTierSocketImpl()
|
||||
{
|
||||
if ((zfd > -1) | (zfd4 > -1) | (zfd6 > -1)) { return; }
|
||||
try {
|
||||
create(true);
|
||||
} catch (Exception x) {
|
||||
System.err.println("error creating ZeroTierSocketImpl instance: " + x);
|
||||
}
|
||||
in.zfd = zfd;
|
||||
out.zfd = zfd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor to be called when an underlying ZeroTier socket already exists (does not create a new ZeroTierSocketImpl)
|
||||
*/
|
||||
public ZeroTierSocketImpl(int fd) {
|
||||
setNativeFileDescriptor(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new ZeroTier socket in the native layer
|
||||
*/
|
||||
protected void create(boolean stream)
|
||||
throws IOException
|
||||
{
|
||||
/*
|
||||
* The native-layer socket is only created once a connect/bind call is made, this is due to the fact
|
||||
* that beforehand we have no way to determine whether we should create an AF_INET or AF_INET6 socket,
|
||||
* as a result, this method intentionally does nothing.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates the underlying libzt socket.
|
||||
*
|
||||
* This does the real work that can't be done in the constructor. This is because the socket address type
|
||||
* isn't known until a connect() or bind() request is given. Additionally we cache the value provided by any
|
||||
* setSoTimeout() calls and implement it immediately after creation.
|
||||
*/
|
||||
private void createAppropriateSocketImpl(InetAddress addr)
|
||||
throws IOException
|
||||
{
|
||||
if ((zfd > -1) | (zfd4 > -1) | (zfd6 > -1)) {
|
||||
return; // Socket already created
|
||||
}
|
||||
if(addr instanceof Inet4Address) {
|
||||
if ((zfd4 = ZeroTier.socket(ZeroTier.AF_INET, ZeroTier.SOCK_STREAM, defaultProtocol)) < 0) {
|
||||
throw new IOException("socket(), errno="+zfd4+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
setCachedSocketOptions(zfd4);
|
||||
}
|
||||
/*
|
||||
* Since Java creates sockets capable of handling IPV4 and IPV6, we must simulate this. We do this by
|
||||
* creating two sockets (one of each type)
|
||||
*/
|
||||
if(addr instanceof Inet6Address) {
|
||||
if ((zfd4 = ZeroTier.socket(ZeroTier.AF_INET, ZeroTier.SOCK_STREAM, defaultProtocol)) < 0) {
|
||||
throw new IOException("socket(), errno="+zfd4+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
if ((zfd6 = ZeroTier.socket(ZeroTier.AF_INET6, ZeroTier.SOCK_STREAM, defaultProtocol)) < 0) {
|
||||
throw new IOException("socket(), errno="+zfd6+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
setCachedSocketOptions(zfd4);
|
||||
setCachedSocketOptions(zfd6);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the remote address the socket is connected to
|
||||
*/
|
||||
protected InetAddress getInetAddress()
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
/*
|
||||
* Connects the socket to a remote address
|
||||
*/
|
||||
protected void connect(String host, int port)
|
||||
throws IOException
|
||||
{
|
||||
// TODO: Refactor and consolidate the connect() logic for all three methods
|
||||
createAppropriateSocketImpl(InetAddress.getByName(host));
|
||||
if ((zfd4 < 0) & (zfd6 < 0)) {
|
||||
throw new IOException("invalid fd");
|
||||
}
|
||||
int err;
|
||||
InetAddress address = InetAddress.getByName(host);
|
||||
ZeroTierSocketAddress zt_addr = new ZeroTierSocketAddress(host, port);
|
||||
if (address instanceof Inet4Address) {
|
||||
if ((err = ZeroTier.connect(zfd4, zt_addr)) < 0) {
|
||||
throw new IOException("connect(), errno="+err+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
setNativeFileDescriptor(zfd4);
|
||||
}
|
||||
if (address instanceof Inet6Address) {
|
||||
if ((err = ZeroTier.connect(zfd6, zt_addr)) < 0) {
|
||||
throw new IOException("connect(), errno="+err+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
setNativeFileDescriptor(zfd6);
|
||||
}
|
||||
super.port = port;
|
||||
}
|
||||
|
||||
/*
|
||||
* Connects the socket to a remote address
|
||||
*/
|
||||
protected void connect(InetAddress address, int port)
|
||||
throws IOException
|
||||
{
|
||||
// TODO: Refactor and consolidate the connect() logic for all three methods
|
||||
createAppropriateSocketImpl(address);
|
||||
if ((zfd4 < 0) & (zfd6 < 0)) {
|
||||
throw new IOException("invalid fd");
|
||||
}
|
||||
int err;
|
||||
ZeroTierSocketAddress zt_addr = new ZeroTierSocketAddress(address.getHostAddress(), port);
|
||||
if (address instanceof Inet4Address) {
|
||||
if ((err = ZeroTier.connect(zfd4, zt_addr)) < 0) {
|
||||
throw new IOException("connect(), errno="+err+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
setNativeFileDescriptor(zfd4);
|
||||
}
|
||||
if (address instanceof Inet6Address) {
|
||||
if ((err = ZeroTier.connect(zfd6, zt_addr)) < 0) {
|
||||
throw new IOException("connect(), errno="+err+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
setNativeFileDescriptor(zfd6);
|
||||
}
|
||||
super.port = port;
|
||||
}
|
||||
|
||||
/*
|
||||
* Connects the socket to a remote address
|
||||
*/
|
||||
protected void connect(SocketAddress address, int timeout)
|
||||
throws IOException
|
||||
{
|
||||
// TODO: Refactor and consolidate the connect() logic for all three methods
|
||||
//System.out.println("host="+((InetSocketAddress)address).getHostString()+", port="+((InetSocketAddress)address).getPort() + ", timeout="+timeout);
|
||||
createAppropriateSocketImpl(((InetSocketAddress)address).getAddress());
|
||||
if ((zfd4 < 0) & (zfd6 < 0)) {
|
||||
throw new IOException("invalid fd");
|
||||
}
|
||||
ZeroTierSocketAddress zt_addr = null;
|
||||
int err;
|
||||
int port = ((InetSocketAddress)address).getPort();
|
||||
if (((InetSocketAddress)address).getAddress() instanceof Inet4Address) {
|
||||
zt_addr = new ZeroTierSocketAddress(((InetSocketAddress)address).getHostString(), ((InetSocketAddress)address).getPort());
|
||||
if ((err = ZeroTier.connect(zfd4, zt_addr)) < 0) {
|
||||
throw new IOException("connect("+zfd4+"), errno="+err+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
setNativeFileDescriptor(zfd4);
|
||||
}
|
||||
if (((InetSocketAddress)address).getAddress() instanceof Inet6Address) {
|
||||
zt_addr = new ZeroTierSocketAddress(((InetSocketAddress)address).getHostString(), ((InetSocketAddress)address).getPort());
|
||||
if ((err = ZeroTier.connect(zfd6, zt_addr)) < 0) {
|
||||
throw new IOException("connect("+zfd6+"), errno="+err+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
setNativeFileDescriptor(zfd6);
|
||||
}
|
||||
super.port = port;
|
||||
}
|
||||
|
||||
/*
|
||||
* Binds the socket to a local address.
|
||||
*
|
||||
* If this gets a bind() request on [::] it will create a both an IPv4 and an IPv6
|
||||
* socket. This is because we might receive a subsequent listen() and accept() request
|
||||
* and want to accept an IPv6 connection. (or) we may get a connect() request with
|
||||
* an IPv4 address. In the latter case we must abandon the IPv6 socket and use the IPv4
|
||||
* socket exclusively.
|
||||
*/
|
||||
protected void bind(InetAddress host, int port)
|
||||
throws IOException
|
||||
{
|
||||
createAppropriateSocketImpl(host);
|
||||
/*
|
||||
After this point we may have either a) created a single IPv4 socket, or b) created
|
||||
an IPv4 and IPv6 socket in anticipation of either verion being used
|
||||
*/
|
||||
//System.out.println("host="+host.toString()+", port="+port);
|
||||
int err;
|
||||
if ((zfd < 0) & (zfd4 < 0) & (zfd6 < 0)) {
|
||||
throw new IOException("invalid fd");
|
||||
}
|
||||
ZeroTierSocketAddress zt_addr = new ZeroTierSocketAddress(host.getHostAddress(), port);
|
||||
|
||||
if (zfd6 > -1) {
|
||||
// Since an IPv6 socket and accept IPv4 connections we will only bind to this address
|
||||
if ((err = ZeroTier.bind(zfd6, zt_addr)) < 0) {
|
||||
throw new IOException("bind("+zfd6+"), errno="+err+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
super.localport = port;
|
||||
return;
|
||||
}
|
||||
if (zfd4 > -1) {
|
||||
// Otherwise, just bind to the regular IPv4 address
|
||||
if ((err = ZeroTier.bind(zfd4, zt_addr)) < 0) {
|
||||
throw new IOException("bind("+zfd4+"), errno="+err+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
super.localport = port;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Puts the socket into a listening state.
|
||||
*
|
||||
* We listen on the IPv6 socket since it can listen for IPv4 connections
|
||||
*/
|
||||
protected void listen(int backlog)
|
||||
throws IOException
|
||||
{
|
||||
int err;
|
||||
if ((zfd6 < 0) | (backlog < 0)) {
|
||||
throw new IOException("invalid fd and/or backlog");
|
||||
}
|
||||
if ((err = ZeroTier.listen(zfd6, backlog)) < 0) {
|
||||
throw new IOException("listen("+zfd6+"), errno="+err+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Accepts an incoming connection.
|
||||
*
|
||||
* We accept on the IPv6 socket since it can accept IPv4 connections
|
||||
*/
|
||||
protected void accept(SocketImpl si)
|
||||
throws IOException
|
||||
{
|
||||
if (zfd6 < 0) {
|
||||
throw new IOException("invalid fd");
|
||||
}
|
||||
int accetpedFd = -1;
|
||||
ZeroTierSocketAddress addr = new ZeroTierSocketAddress();
|
||||
if ((accetpedFd = ZeroTier.accept(zfd6, addr)) < 0) {
|
||||
throw new IOException("accept("+zfd6+"), errno="+accetpedFd+", see: libzt/ext/lwip/src/include/errno.h");
|
||||
}
|
||||
// Give the new socket fd from the native layer to the new unconnected ZeroTierSocketImpl
|
||||
((ZeroTierSocketImpl)si).setFileDescriptor(accetpedFd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the input stream for this socket
|
||||
*/
|
||||
protected ZeroTierInputStream getInputStream()
|
||||
throws IOException
|
||||
{
|
||||
if (in == null) {
|
||||
throw new IOException();
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the output stream for this socket
|
||||
*/
|
||||
protected ZeroTierOutputStream getOutputStream()
|
||||
throws IOException
|
||||
{
|
||||
if (out == null) {
|
||||
throw new IOException();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the remote port to which this socket is connected
|
||||
*/
|
||||
protected int getPort()
|
||||
{
|
||||
return super.port;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the local port to which this socket is bound
|
||||
*/
|
||||
protected int getLocalPort()
|
||||
{
|
||||
return super.localport;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether this socket implementation supports urgent data (hint: it doesn't)
|
||||
*/
|
||||
protected boolean supportsUrgentData()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void setSocket(ZeroTierSocket soc)
|
||||
{
|
||||
this.socket = soc;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
Socket getSocket()
|
||||
{
|
||||
return socket;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
/*
|
||||
void setServerSocket(ZeroTierServerSocket soc)
|
||||
{
|
||||
this.serverSocket = soc;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
ServerSocket getServerSocket()
|
||||
{
|
||||
return serverSocket;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of bytes that can be read from the socket without blocking
|
||||
*/
|
||||
protected int available()
|
||||
throws IOException
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes the socket
|
||||
*/
|
||||
protected void close()
|
||||
throws IOException
|
||||
{
|
||||
if (zfd > -1) {
|
||||
ZeroTier.close(zfd);
|
||||
}
|
||||
if (zfd4 > -1) {
|
||||
ZeroTier.close(zfd4);
|
||||
}
|
||||
if (zfd6 > -1) {
|
||||
ZeroTier.close(zfd6);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Send one byte of urgent data on the socket
|
||||
*/
|
||||
protected void sendUrgentData(int data)
|
||||
throws IOException
|
||||
{
|
||||
System.err.println("sendUrgentData: ZeroTierSocketImpl does not currently support this feature");
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets some specified socket option
|
||||
*/
|
||||
public Object getOption(int optID)
|
||||
throws SocketException
|
||||
{
|
||||
// Call native layer
|
||||
ZeroTierSocketOptionValue optval = new ZeroTierSocketOptionValue();
|
||||
int option = -1;
|
||||
int level = -1;
|
||||
|
||||
if (zfd < 0) { // If we haven't committed to a socket version yet, cache the value
|
||||
if (optID == SocketOptions.SO_TIMEOUT || optID == ZeroTier.SO_RCVTIMEO) {
|
||||
return Integer.valueOf(_so_rcvtimeo);
|
||||
}
|
||||
if (optID == SocketOptions.SO_KEEPALIVE || optID == ZeroTier.SO_KEEPALIVE) {
|
||||
return Boolean.valueOf(_so_keepalive);
|
||||
}
|
||||
if (optID == SocketOptions.SO_SNDBUF || optID == ZeroTier.SO_SNDBUF) {
|
||||
return Integer.valueOf(_so_sndbuf);
|
||||
}
|
||||
if (optID == SocketOptions.SO_RCVBUF || optID == ZeroTier.SO_RCVBUF) {
|
||||
return Integer.valueOf(_so_rcvbuf);
|
||||
}
|
||||
if (optID == SocketOptions.SO_REUSEADDR || optID == ZeroTier.SO_REUSEADDR) {
|
||||
return Boolean.valueOf(_so_reuseaddr);
|
||||
}
|
||||
if (optID == SocketOptions.SO_LINGER || optID == ZeroTier.SO_LINGER) {
|
||||
return Integer.valueOf(_so_linger);
|
||||
}
|
||||
if (optID == SocketOptions.IP_TOS || optID == ZeroTier.IP_TOS) {
|
||||
return Integer.valueOf(_so_tos);
|
||||
}
|
||||
if (optID == SocketOptions.TCP_NODELAY || optID == ZeroTier.TCP_NODELAY) {
|
||||
return Boolean.valueOf(_so_nodelay);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (optID == SocketOptions.SO_TIMEOUT || optID == ZeroTier.SO_RCVTIMEO) {
|
||||
option = ZeroTier.SO_RCVTIMEO;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
}
|
||||
if (optID == SocketOptions.SO_KEEPALIVE || optID == ZeroTier.SO_KEEPALIVE) {
|
||||
option = ZeroTier.SO_KEEPALIVE;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
}
|
||||
if (optID == SocketOptions.SO_SNDBUF || optID == ZeroTier.SO_SNDBUF) {
|
||||
option = ZeroTier.SO_SNDBUF;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
}
|
||||
if (optID == SocketOptions.SO_RCVBUF || optID == ZeroTier.SO_RCVBUF) {
|
||||
option = ZeroTier.SO_RCVBUF;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
}
|
||||
if (optID == SocketOptions.SO_REUSEADDR || optID == ZeroTier.SO_REUSEADDR) {
|
||||
option = ZeroTier.SO_REUSEADDR;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
}
|
||||
if (optID == SocketOptions.SO_LINGER || optID == ZeroTier.SO_LINGER) {
|
||||
option = ZeroTier.SO_LINGER;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
}
|
||||
// IP
|
||||
if (optID == SocketOptions.IP_TOS || optID == ZeroTier.IP_TOS) {
|
||||
option = ZeroTier.IP_TOS;
|
||||
level = ZeroTier.IPPROTO_IP;
|
||||
}
|
||||
// TCP
|
||||
if (optID == SocketOptions.TCP_NODELAY || optID == ZeroTier.TCP_NODELAY) {
|
||||
option = ZeroTier.TCP_NODELAY;
|
||||
level = ZeroTier.IPPROTO_TCP;
|
||||
}
|
||||
ZeroTier.getsockopt(zfd, level, option, optval);
|
||||
// Convert native layer's response into Java object of some sort
|
||||
if (optval.isBoolean) {
|
||||
return Boolean.valueOf(optval.booleanValue);
|
||||
}
|
||||
if (optval.isInteger) {
|
||||
return Integer.valueOf(optval.integerValue);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets a socket option to a specified value. This method should be able to handle SocketOptions values
|
||||
* as well as native ZeroTier.* options
|
||||
*/
|
||||
public void setOption(int optID, Object value)
|
||||
throws SocketException
|
||||
{
|
||||
if (value == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
int option = -1;
|
||||
int level = -1;
|
||||
|
||||
ZeroTierSocketOptionValue optval = new ZeroTierSocketOptionValue();
|
||||
|
||||
if (zfd < 0) { // If we haven't committed to a socket version yet, cache the value
|
||||
if (optID == SocketOptions.SO_TIMEOUT || optID == ZeroTier.SO_RCVTIMEO) {
|
||||
_so_rcvtimeo = ((Integer)value).intValue(); return;
|
||||
}
|
||||
if (optID == SocketOptions.SO_KEEPALIVE || optID == ZeroTier.SO_KEEPALIVE) {
|
||||
_so_keepalive = ((Boolean)value).booleanValue() ? true : false; return;
|
||||
}
|
||||
if (optID == SocketOptions.SO_SNDBUF || optID == ZeroTier.SO_SNDBUF) {
|
||||
_so_sndbuf = ((Integer)value).intValue(); return;
|
||||
}
|
||||
if (optID == SocketOptions.SO_RCVBUF || optID == ZeroTier.SO_RCVBUF) {
|
||||
_so_rcvbuf = ((Integer)value).intValue(); return;
|
||||
}
|
||||
if (optID == SocketOptions.SO_REUSEADDR || optID == ZeroTier.SO_REUSEADDR) {
|
||||
_so_reuseaddr = ((Boolean)value).booleanValue() ? true : false; return;
|
||||
}
|
||||
if (optID == SocketOptions.SO_LINGER || optID == ZeroTier.SO_LINGER) {
|
||||
_so_linger = ((Integer)value).intValue(); return;
|
||||
}
|
||||
if (optID == SocketOptions.IP_TOS || optID == ZeroTier.IP_TOS) {
|
||||
_so_tos = ((Integer)value).intValue(); return;
|
||||
}
|
||||
if (optID == SocketOptions.TCP_NODELAY || optID == ZeroTier.TCP_NODELAY) {
|
||||
_so_nodelay = ((Boolean)value).booleanValue() ? true : false; return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// SOL
|
||||
if (optID == SocketOptions.SO_TIMEOUT || optID == ZeroTier.SO_RCVTIMEO) {
|
||||
option = ZeroTier.SO_RCVTIMEO;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
if (value instanceof Integer) {
|
||||
optval.isInteger = true;
|
||||
optval.integerValue = ((Integer)value).intValue();
|
||||
}
|
||||
}
|
||||
|
||||
if (optID == SocketOptions.SO_KEEPALIVE || optID == ZeroTier.SO_KEEPALIVE) {
|
||||
option = ZeroTier.SO_KEEPALIVE;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
if (value instanceof Integer) {
|
||||
optval.isBoolean = true;
|
||||
optval.booleanValue = ((Boolean)value).booleanValue() ? true : false;
|
||||
}
|
||||
}
|
||||
if (optID == SocketOptions.SO_SNDBUF || optID == ZeroTier.SO_SNDBUF) {
|
||||
option = ZeroTier.SO_SNDBUF;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
if (value instanceof Integer) {
|
||||
optval.isInteger = true;
|
||||
optval.integerValue = ((Integer)value).intValue();
|
||||
}
|
||||
}
|
||||
if (optID == SocketOptions.SO_RCVBUF || optID == ZeroTier.SO_RCVBUF) {
|
||||
option = ZeroTier.SO_RCVBUF;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
if (value instanceof Integer) {
|
||||
optval.isInteger = true;
|
||||
optval.integerValue = ((Integer)value).intValue();
|
||||
}
|
||||
}
|
||||
if (optID == SocketOptions.SO_REUSEADDR || optID == ZeroTier.SO_REUSEADDR) {
|
||||
option = ZeroTier.SO_REUSEADDR;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
if (value instanceof Integer) {
|
||||
optval.isBoolean = true;
|
||||
optval.booleanValue = ((Boolean)value).booleanValue() ? true : false;
|
||||
}
|
||||
}
|
||||
if (optID == SocketOptions.SO_LINGER || optID == ZeroTier.SO_LINGER) {
|
||||
option = ZeroTier.SO_LINGER;
|
||||
level = ZeroTier.SOL_SOCKET;
|
||||
if (value instanceof Integer) {
|
||||
optval.isInteger = true;
|
||||
optval.integerValue = ((Integer)value).intValue();
|
||||
}
|
||||
}
|
||||
// IP
|
||||
if (optID == SocketOptions.IP_TOS || optID == ZeroTier.IP_TOS) {
|
||||
option = ZeroTier.IP_TOS;
|
||||
level = ZeroTier.IPPROTO_IP;
|
||||
if (value instanceof Integer) {
|
||||
optval.isInteger = true;
|
||||
optval.integerValue = ((Integer)value).intValue();
|
||||
}
|
||||
}
|
||||
// TCP
|
||||
if (optID == SocketOptions.TCP_NODELAY || optID == ZeroTier.TCP_NODELAY) {
|
||||
option = ZeroTier.TCP_NODELAY;
|
||||
level = ZeroTier.IPPROTO_TCP;
|
||||
if (value instanceof Integer) {
|
||||
optval.isBoolean = true;
|
||||
optval.booleanValue = ((Boolean)value).booleanValue() ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
if (option < 0) { // No option was properly set
|
||||
//throw new UnsupportedOperationException();
|
||||
}
|
||||
ZeroTier.setsockopt(zfd, level, option, optval);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Disables the input aspect of the socket
|
||||
*/
|
||||
public void shutdownInput()
|
||||
{
|
||||
ZeroTier.shutdown(zfd, ZeroTier.SHUT_RD);
|
||||
// Alternatively: getInputStream().close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Disables the output aspect of the socket
|
||||
*/
|
||||
public void shutdownOutput()
|
||||
{
|
||||
ZeroTier.shutdown(zfd, ZeroTier.SHUT_WR);
|
||||
// Alternatively: getOutputStream().close();
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the file descriptor
|
||||
*/
|
||||
public void setFileDescriptor(int fd)
|
||||
{
|
||||
zfd = fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resets the socket
|
||||
*/
|
||||
void reset()
|
||||
throws IOException
|
||||
{
|
||||
localport = 0;
|
||||
address = null;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
public FileDescriptor getFileDescriptor()
|
||||
{
|
||||
// TODO: Should probably remove this for production
|
||||
System.out.println("getFileDescriptor(), zfd="+zfd);
|
||||
ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(zfd);
|
||||
return pfd.getFileDescriptor();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
import com.zerotier.libzt.ZeroTier;
|
||||
import com.zerotier.libzt.ZeroTierSocketImpl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import javax.net.SocketFactory;
|
||||
import java.net.SocketImplFactory;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
public class ZeroTierSocketImplFactory implements SocketImplFactory
|
||||
{
|
||||
/*
|
||||
* Does nothing
|
||||
*/
|
||||
public ZeroTierSocketImplFactory() { }
|
||||
|
||||
/*
|
||||
* Produces a ZeroTierSocketImpl
|
||||
*/
|
||||
public ZeroTierSocketImpl createSocketImpl()
|
||||
{
|
||||
return new ZeroTierSocketImpl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.zerotier.libzt;
|
||||
|
||||
public class ZeroTierSocketOptionValue
|
||||
{
|
||||
public boolean isBoolean = false;
|
||||
public boolean booleanValue;
|
||||
|
||||
public boolean isInteger = false;
|
||||
public int integerValue;
|
||||
|
||||
public boolean isTimeval = false;
|
||||
public int tv_sec; // seconds
|
||||
public int tv_usec; // microseconds
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
</vector>
|
||||
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8" />
|
||||
</vector>
|
||||
19
ports/android/app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sample_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
BIN
ports/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
ports/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
ports/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
ports/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
ports/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
BIN
ports/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 10 KiB |
BIN
ports/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 15 KiB |
6
ports/android/app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
</resources>
|
||||
3
ports/android/app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">ZeroTier</string>
|
||||
</resources>
|
||||
11
ports/android/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.example.zerotier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
27
ports/android/build.gradle
Normal file
@@ -0,0 +1,27 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
13
ports/android/gradle.properties
Normal file
@@ -0,0 +1,13 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
BIN
ports/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
ports/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Wed Jul 18 09:57:38 PDT 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||
172
ports/android/gradlew
vendored
Executable file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
ports/android/gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
1
ports/android/settings.gradle
Normal file
@@ -0,0 +1 @@
|
||||
include ':app'
|
||||
4
ports/clean.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
rd /S /Q bin
|
||||
rd /S /Q build
|
||||
rd /S /Q WinBuild32
|
||||
rd /S /Q WinBuild64
|
||||
7
ports/clean.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -rf bin build products tmp
|
||||
rm -f *.o *.s *.exp *.lib .depend* *.core core
|
||||
rm -rf .depend
|
||||
find . -type f \( -name '*.o' -o -name '*.o.d' -o -name \
|
||||
'*.out' -o -name '*.log' -o -name '*.dSYM' -o -name '*.class' \) -delete
|
||||
88
ports/dist.bat
Normal file
@@ -0,0 +1,88 @@
|
||||
REM Build all target configurations and copy results into "prebuilt"
|
||||
|
||||
set PrebuiltDebugWin32Dir=staging\debug\win32
|
||||
set PrebuiltDebugWin64Dir=staging\debug\win64
|
||||
set PrebuiltReleaseWin32Dir=staging\release\win32
|
||||
set PrebuiltReleaseWin64Dir=staging\release\win64
|
||||
|
||||
mkdir %PrebuiltDebugWin32Dir%
|
||||
mkdir %PrebuiltDebugWin64Dir%
|
||||
mkdir %PrebuiltReleaseWin32Dir%
|
||||
mkdir %PrebuiltReleaseWin64Dir%
|
||||
|
||||
set DebugWinBuildDir=bin\lib\Debug
|
||||
set ReleaseWinBuildDir=bin\lib\Release
|
||||
|
||||
mkdir WinBuild32 & pushd WinBuild32
|
||||
cmake -G "Visual Studio 15 2017" ../
|
||||
popd
|
||||
mkdir WinBuild64 & pushd WinBuild64
|
||||
cmake -G "Visual Studio 15 2017 Win64" ../
|
||||
popd
|
||||
|
||||
cmake --build WinBuild32 --config Release
|
||||
cmake --build WinBuild32 --config Debug
|
||||
|
||||
copy %DebugWinBuildDir%\zt-static.lib %PrebuiltDebugWin32Dir%\zt.lib
|
||||
copy %DebugWinBuildDir%\zt-shared.dll %PrebuiltDebugWin32Dir%\zt.dll
|
||||
copy %ReleaseWinBuildDir%\zt-static.lib %PrebuiltReleaseWin32Dir%\zt.lib
|
||||
copy %ReleaseWinBuildDir%\zt-shared.dll %PrebuiltReleaseWin32Dir%\zt.dll
|
||||
|
||||
cmake --build WinBuild64 --config Release
|
||||
cmake --build WinBuild64 --config Debug
|
||||
|
||||
copy %DebugWinBuildDir%\zt-static.lib %PrebuiltDebugWin64Dir%\zt.lib
|
||||
copy %DebugWinBuildDir%\zt-shared.dll %PrebuiltDebugWin64Dir%\zt.dll
|
||||
copy %ReleaseWinBuildDir%\zt-static.lib %PrebuiltReleaseWin64Dir%\zt.lib
|
||||
copy %ReleaseWinBuildDir%\zt-shared.dll %PrebuiltReleaseWin64Dir%\zt.dll
|
||||
|
||||
rd /S /Q bin
|
||||
|
||||
# Build with JNI
|
||||
|
||||
mkdir WinBuild32 & pushd WinBuild32
|
||||
cmake -D JNI:BOOL=ON -G "Visual Studio 15 2017" ../
|
||||
popd
|
||||
mkdir WinBuild64 & pushd WinBuild64
|
||||
cmake -D JNI:BOOL=ON -G "Visual Studio 15 2017 Win64" ../
|
||||
popd
|
||||
|
||||
cmake --build WinBuild32 --config Release
|
||||
cmake --build WinBuild32 --config Debug
|
||||
|
||||
REM Build JAR file
|
||||
REM release variant
|
||||
cd packages\java
|
||||
del com/zerotier/libzt/*.class
|
||||
move ..\..\%ReleaseWinBuildDir%\zt-shared.dll zt.dll
|
||||
javac com/zerotier/libzt/*.java
|
||||
jar cf zt.jar zt.dll com/zerotier/libzt/*.class
|
||||
move zt.jar ..\..\%PrebuiltReleaseWin32Dir%
|
||||
REM debug variant
|
||||
del com/zerotier/libzt/*.class
|
||||
move ..\..\%DebugWinBuildDir%\zt-shared.dll zt.dll
|
||||
javac com/zerotier/libzt/*.java
|
||||
jar cf zt.jar zt.dll com/zerotier/libzt/*.class
|
||||
move zt.jar ..\..\%PrebuiltDebugWin32Dir%
|
||||
popd
|
||||
popd
|
||||
|
||||
cmake --build WinBuild64 --config Release
|
||||
cmake --build WinBuild64 --config Debug
|
||||
|
||||
REM Build JAR file
|
||||
REM release variant
|
||||
cd packages\java
|
||||
del com/zerotier/libzt/*.class
|
||||
move ..\..\%ReleaseWinBuildDir%\zt-shared.dll zt.dll
|
||||
javac com/zerotier/libzt/*.java
|
||||
jar cf zt.jar zt.dll com/zerotier/libzt/*.class
|
||||
move zt.jar ..\..\%PrebuiltReleaseWin64Dir%
|
||||
REM debug variant
|
||||
del com/zerotier/libzt/*.class
|
||||
move ..\..\%DebugWinBuildDir%\zt-shared.dll zt.dll
|
||||
javac com/zerotier/libzt/*.java
|
||||
jar cf zt.jar zt.dll com/zerotier/libzt/*.class
|
||||
move zt.jar ..\..\%PrebuiltDebugWin64Dir%
|
||||
popd
|
||||
popd
|
||||
184
ports/dist.sh
Executable file
@@ -0,0 +1,184 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Call this script from the root project directory via `make dist`
|
||||
# - submodules will be recursively initialized and updated
|
||||
# - patches will be applied to submodules if needed
|
||||
# - this script will call CMake to generate library-building packages if necessary
|
||||
# - once projects have been generated, this script will use their tooling to build the libraries/packages
|
||||
# - when all products have been built and moved to `tmp`, they will be compressed and moved to `products`
|
||||
|
||||
OSNAME=$(uname | tr '[A-Z]' '[a-z]')
|
||||
BUILD_CONCURRENCY=4
|
||||
PROJROOT=$(pwd)
|
||||
BUILD_PRODUCTS_DIR=$(pwd)/bin
|
||||
LIB_PRODUCTS_DIR=$BUILD_PRODUCTS_DIR/lib
|
||||
FINISHED_PRODUCTS_DIR=$(pwd)/products
|
||||
STAGING_DIR=$(pwd)/staging
|
||||
# Windows (previously built)
|
||||
WIN_PREBUILT_DIR=$PROJROOT/staging/win
|
||||
WIN_RELEASE_PRODUCTS_DIR=$WIN_PREBUILT_DIR/release
|
||||
WIN_DEBUG_PRODUCTS_DIR=$WIN_PREBUILT_DIR/debug
|
||||
WIN32_RELEASE_PRODUCTS_DIR=$WIN_RELEASE_PRODUCTS_DIR/win32
|
||||
WIN64_RELEASE_PRODUCTS_DIR=$WIN_RELEASE_PRODUCTS_DIR/win64
|
||||
WIN32_DEBUG_PRODUCTS_DIR=$WIN_DEBUG_PRODUCTS_DIR/win32
|
||||
WIN64_DEBUG_PRODUCTS_DIR=$WIN_DEBUG_PRODUCTS_DIR/win64
|
||||
# Linux
|
||||
LINUX_PROD_DIR=$PROJROOT/staging/linux
|
||||
# macOS
|
||||
MACOS_PROD_DIR=$PROJROOT/staging/macos
|
||||
MACOS_RELEASE_PROD_DIR=$MACOS_PROD_DIR/release
|
||||
MACOS_DEBUG_PROD_DIR=$MACOS_PROD_DIR/debug
|
||||
# iOS
|
||||
IOS_PROD_DIR=$PROJROOT/staging/ios
|
||||
# Android
|
||||
ANDROID_PROJ_DIR=$(pwd)/"ports/android"
|
||||
ANDROID_ARCHIVE_FILENAME="zt.aar"
|
||||
# Xcode
|
||||
XCODE_IOS_PROJ_DIR=$(pwd)/"ports/xcode_ios"
|
||||
XCODE_MACOS_PROJ_DIR=$(pwd)/"ports/xcode_macos"
|
||||
|
||||
mkdir $FINISHED_PRODUCTS_DIR
|
||||
mkdir $STAGING_DIR
|
||||
|
||||
# Check that projects exist, generate them and exit if they don't exist
|
||||
generate_projects_if_necessary()
|
||||
{
|
||||
if [[ $OSNAME = *"darwin"* ]]; then
|
||||
# iOS
|
||||
if [ ! -d "$XCODE_IOS_PROJ_DIR" ]; then
|
||||
echo "BUILDING: iOS project"
|
||||
should_exit=1
|
||||
mkdir -p $XCODE_IOS_PROJ_DIR
|
||||
cd $XCODE_IOS_PROJ_DIR
|
||||
cmake -G Xcode ../../
|
||||
# Bug in CMake requires us to manually replace architecture strings in project file
|
||||
sed -i '' 's/x86_64/$(CURRENT_ARCH)/g' $PROJNAME.xcodeproj/project.pbxproj
|
||||
cd -
|
||||
fi
|
||||
# macOS
|
||||
if [ ! -d "$XCODE_MACOS_PROJ_DIR" ]; then
|
||||
echo "BUILDING: macOS project"
|
||||
should_exit=1
|
||||
mkdir -p $XCODE_MACOS_PROJ_DIR
|
||||
cd $XCODE_MACOS_PROJ_DIR
|
||||
cmake -G Xcode ../../
|
||||
cd -
|
||||
fi
|
||||
# android?
|
||||
if [[ $should_exit = 1 ]]; then
|
||||
echo "Generated projects. Perform necessary modifications and then re-run this script"
|
||||
echo "Please place previously built windows binaries in $WIN_PREBUILT_DIR before running again."
|
||||
exit 0
|
||||
else
|
||||
echo "Projects detected, going to build stage next"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
build_all_products()
|
||||
{
|
||||
CONFIG=$1
|
||||
UPPERCASE_CONFIG="$(tr '[:lower:]' '[:upper:]' <<< ${1:0:1})${1:1}"
|
||||
|
||||
# Targets to build on and for darwin
|
||||
if [[ $OSNAME = *"darwin"* ]]; then
|
||||
# Xcode Frameworks --- Builds targets from a CMake-generated Xcode project
|
||||
if false; then
|
||||
if [[ $2 != *"JNI"* ]]; then
|
||||
CURR_BUILD_PRODUCTS_DIR=$LIB_PRODUCTS_DIR/$UPPERCASE_CONFIG
|
||||
# (iOS)
|
||||
echo "BUILDING: iOS"
|
||||
cd $XCODE_IOS_PROJ_DIR
|
||||
xcodebuild -target zt -configuration "$UPPERCASE_CONFIG" -sdk "iphoneos"
|
||||
xcodebuild -target zt-static -configuration "$UPPERCASE_CONFIG" -sdk "iphoneos"
|
||||
cd -
|
||||
CURR_ARCH="arm64" # spoof this architecture since HOSTTYPE is likely x86_64
|
||||
CURR_TMP_PRODUCT_DIR=$STAGING_DIR/$CONFIG/ios-$CURR_ARCH
|
||||
mkdir -p $CURR_TMP_PRODUCT_DIR
|
||||
mv $CURR_BUILD_PRODUCTS_DIR/*.framework $CURR_TMP_PRODUCT_DIR
|
||||
mv $CURR_BUILD_PRODUCTS_DIR/libzt.* $CURR_TMP_PRODUCT_DIR
|
||||
|
||||
# (macOS)
|
||||
echo "BUILDING: macOS"
|
||||
cd $XCODE_MACOS_PROJ_DIR
|
||||
xcodebuild -target zt -configuration "$UPPERCASE_CONFIG" -sdk "macosx"
|
||||
xcodebuild -target zt-static -configuration "$UPPERCASE_CONFIG" -sdk "macosx"
|
||||
xcodebuild -target zt-shared -configuration "$UPPERCASE_CONFIG" -sdk "macosx"
|
||||
cd -
|
||||
CURR_TMP_PRODUCT_DIR=$STAGING_DIR/$CONFIG/macos-$(uname -m)
|
||||
mkdir -p $CURR_TMP_PRODUCT_DIR
|
||||
mv $CURR_BUILD_PRODUCTS_DIR/*.framework $CURR_TMP_PRODUCT_DIR
|
||||
mv $CURR_BUILD_PRODUCTS_DIR/libzt.* $CURR_TMP_PRODUCT_DIR
|
||||
fi
|
||||
fi
|
||||
# Android Archive (AAR) --- Executes a Gradle task
|
||||
if true; then
|
||||
CMAKE_FLAGS=$CMAKE_FLAGS" -DSDK_JNI=1"
|
||||
CURR_ARCH="armeabi-v7a" # spoof this architecture since HOSTTYPE is likely x86_64
|
||||
CURR_TMP_PRODUCT_DIR=$STAGING_DIR/$CONFIG/android-$CURR_ARCH
|
||||
mkdir -p $CURR_TMP_PRODUCT_DIR
|
||||
echo "BUILDING: AAR"
|
||||
cd $ANDROID_PROJ_DIR
|
||||
./gradlew assemble$UPPERCASE_CONFIG # e.g. assembleRelease
|
||||
mv $ANDROID_PROJ_DIR/app/build/outputs/aar/app-$CONFIG.aar $CURR_TMP_PRODUCT_DIR/$ANDROID_ARCHIVE_FILENAME
|
||||
cd -
|
||||
fi
|
||||
# Java Archive (JAR)
|
||||
if false; then
|
||||
CURR_BUILD_PRODUCTS_DIR=$LIB_PRODUCTS_DIR
|
||||
CMAKE_FLAGS=$CMAKE_FLAGS" -DJNI=1"
|
||||
CURR_TMP_PRODUCT_DIR=$STAGING_DIR/$CONFIG/macos-$(uname -m)
|
||||
mkdir -p $CURR_TMP_PRODUCT_DIR
|
||||
echo "BUILDING: JAR"
|
||||
rm -rf $LIB_PRODUCTS_DIR # clean-lite
|
||||
cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=$CONFIG "-DJNI=1 -DBUILD_TESTS=0"
|
||||
cmake --build build
|
||||
cd $PROJROOT/ports/java
|
||||
cp $CURR_BUILD_PRODUCTS_DIR/libzt.dylib .
|
||||
javac com/zerotier/libzt/*.java
|
||||
jar cf zt.jar libzt.dylib com/zerotier/libzt/*.class
|
||||
rm libzt.dylib
|
||||
mv zt.jar $CURR_TMP_PRODUCT_DIR
|
||||
cd -
|
||||
fi
|
||||
fi
|
||||
# Linux targets
|
||||
if [[ $OSNAME = *"linux"* ]]; then
|
||||
CURR_BUILD_PRODUCTS_DIR=$LIB_PRODUCTS_DIR
|
||||
# Ordinary libraries
|
||||
if true; then
|
||||
rm -rf $LIB_PRODUCTS_DIR
|
||||
cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=$CONFIG "-DBUILD_TESTS=0"
|
||||
cmake --build build
|
||||
# -j $BUILD_CONCURRENCY
|
||||
CURR_TMP_PRODUCT_DIR=$STAGING_DIR/$CONFIG/linux-$(uname -m)
|
||||
mkdir -p $CURR_TMP_PRODUCT_DIR
|
||||
mv $CURR_BUILD_PRODUCTS_DIR/libzt.* $CURR_TMP_PRODUCT_DIR
|
||||
fi
|
||||
# Java JAR file
|
||||
if true; then
|
||||
rm -rf $LIB_PRODUCTS_DIR
|
||||
cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=$CONFIG "-DJNI=1 -DBUILD_TESTS=0"
|
||||
cmake --build build
|
||||
# -j $BUILD_CONCURRENCY
|
||||
CURR_TMP_PRODUCT_DIR=$STAGING_DIR/$CONFIG/linux-$(uname -m)
|
||||
mkdir -p $CURR_TMP_PRODUCT_DIR
|
||||
cd $PROJROOT/ports/java
|
||||
cp $CURR_BUILD_PRODUCTS_DIR/libzt.so .
|
||||
javac com/zerotier/libzt/*.java
|
||||
jar cf zt.jar libzt.dylib com/zerotier/libzt/*.class
|
||||
rm libzt.dylib
|
||||
mv zt.jar $CURR_TMP_PRODUCT_DIR
|
||||
cd -
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
main()
|
||||
{
|
||||
#generate_projects_if_necessary
|
||||
build_all_products "release"
|
||||
build_all_products "debug"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
6
ports/module.modulemap
Normal file
@@ -0,0 +1,6 @@
|
||||
framework module zt {
|
||||
umbrella header "Xcode-Bridging-Header.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
43
ports/package.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
PROJNAME="zt"
|
||||
LIBNAME="lib"$PROJNAME
|
||||
LIBZT_VERSION="1.2.0"
|
||||
LIBZT_REVISION="1"
|
||||
ZT_CORE_VERSION="1.2.12"
|
||||
FILENAME_PREFIX=${LIBNAME}
|
||||
|
||||
STAGING_DIR=$(pwd)/staging
|
||||
STAGING_DEBUG_DIR=$(pwd)/staging/debug
|
||||
STAGING_RELEASE_DIR=$(pwd)/staging/release
|
||||
FINISHED_PRODUCTS_DIR=$(pwd)/products
|
||||
|
||||
# Clean before zipping
|
||||
find . -type f \( -name '*.DS_Store' -o -name 'thumbs.db' \) -delete
|
||||
|
||||
# Emit a README file
|
||||
echo $'* libzt version: '${LIBZT_VERSION}$'r'${LIBZT_REVISION}$'\n* Core ZeroTier version: '${ZT_CORE_VERSION}$'\n* Date: '$(date)$'\n\nZeroTier Manual: https://www.zerotier.com/manual.shtml\n
|
||||
Other Downloads: https://www.zerotier.com/download.shtml
|
||||
\nlibzt Repo: https://github.com/zerotier/libzt\n\nFor more assistance, visit https://my.zerotier.com and ask your question in our Community section' > ${STAGING_DIR}/README.md
|
||||
|
||||
cp ${STAGING_DIR}/README.md ${STAGING_DIR}/debug/README.md
|
||||
cp ${STAGING_DIR}/README.md ${STAGING_DIR}/release/README.md
|
||||
|
||||
# Package everything together
|
||||
# (debug)
|
||||
PRODUCT_FILENAME=${FILENAME_PREFIX}-debug.tar.gz
|
||||
echo "Making: " ${FINISHED_PRODUCTS_DIR}/${PRODUCT_FILENAME}
|
||||
cd ${STAGING_DEBUG_DIR}
|
||||
tar --exclude=${PRODUCT_FILENAME} -zcvf ${PRODUCT_FILENAME} .
|
||||
md5 $PRODUCT_FILENAME
|
||||
mv *.tar.gz ${FINISHED_PRODUCTS_DIR}
|
||||
cd -
|
||||
|
||||
# (release)
|
||||
PRODUCT_FILENAME=${FILENAME_PREFIX}-release.tar.gz
|
||||
echo "Making: " ${FINISHED_PRODUCTS_DIR}/${PRODUCT_FILENAME}
|
||||
cd ${STAGING_RELEASE_DIR}
|
||||
tar --exclude=${PRODUCT_FILENAME} -zcvf ${PRODUCT_FILENAME} .
|
||||
md5 $PRODUCT_FILENAME
|
||||
mv *.tar.gz ${FINISHED_PRODUCTS_DIR}
|
||||
cd -
|
||||