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 -
|
||||||