Added latest Android Java port

This commit is contained in:
Joseph Henry
2019-02-15 03:47:09 -08:00
parent e40c3f5527
commit 1b033242ce
54 changed files with 3356 additions and 0 deletions

10
ports/android/.gitignore vendored Normal file
View 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
View 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>

View File

@@ -0,0 +1,2 @@
connection.project.dir=
eclipse.preferences.version=1

1
ports/android/app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

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

View File

@@ -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());
}
}

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

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

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

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

View File

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

View File

@@ -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);
}
}
}

View File

@@ -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];
}
}

View File

@@ -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();
}
}

View File

@@ -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; }
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

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

View File

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

View File

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

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

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

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">ZeroTier</string>
</resources>

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

View File

@@ -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);
}
}

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

View 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

Binary file not shown.

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

View File

@@ -0,0 +1 @@
include ':app'

4
ports/clean.bat Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,6 @@
framework module zt {
umbrella header "Xcode-Bridging-Header.h"
export *
module * { export * }
}

43
ports/package.sh Executable file
View 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 -