Moved Java sources to src/java

This commit is contained in:
Joseph Henry
2019-03-18 16:08:18 -07:00
parent bfddbc930d
commit 630c3d6331
14 changed files with 2401 additions and 0 deletions

241
src/java/ZeroTier.java Normal file
View File

@@ -0,0 +1,241 @@
/*
* 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
{
//////////////////////////////////////////////////////////////////////////////
// Control API error codes //
//////////////////////////////////////////////////////////////////////////////
// Everything is ok
public static int ZTS_ERR_OK = 0;
// A argument provided by the user application is invalid (e.g. out of range, NULL, etc)
public static int ZTS_ERR_INVALID_ARG = -1;
// The service isn't initialized or is for some reason currently unavailable. Try again.
public static int ZTS_ERR_SERVICE = -2;
// For some reason this API operation is not permitted or doesn't make sense at this time.
public static int ZTS_ERR_INVALID_OP = -3;
// The call succeeded, but no object or relevant result was available
public static int ZTS_ERR_NO_RESULT = -4;
// General internal failure
public static int ZTS_ERR_GENERAL = -5;
//////////////////////////////////////////////////////////////////////////////
// Static initialization //
//////////////////////////////////////////////////////////////////////////////
static
{
// loads libzt.so or libzt.dylib
System.loadLibrary("zt");
// Give the native code a reference to this VM (for callbacks)
if (init() != ZTS_ERR_OK) {
throw new ExceptionInInitializerError("JNI init() failed (see GetJavaVM())");
}
}
//////////////////////////////////////////////////////////////////////////////
// Control API event codes //
//////////////////////////////////////////////////////////////////////////////
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_READY_IP4_IP6 = 39;
public static int EVENT_NETWORK_DOWN = 40;
// 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;
//////////////////////////////////////////////////////////////////////////////
// ZeroTier Constants //
//////////////////////////////////////////////////////////////////////////////
public static int ZT_MAX_PEER_NETWORK_PATHS = 16;
//////////////////////////////////////////////////////////////////////////////
// Socket API Constants //
//////////////////////////////////////////////////////////////////////////////
// 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;
//////////////////////////////////////////////////////////////////////////////
// ZeroTier Service Controls //
//////////////////////////////////////////////////////////////////////////////
public static native int start(String path, ZeroTierEventListener callbackClass, int port);
public static native int 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_node_status();
public static native int get_network_status(long networkId);
public static native int get_peer_status(long peerId);
public static native int get_peer(long peerId, ZeroTierPeerDetails details);
//////////////////////////////////////////////////////////////////////////////
// Socket API //
//////////////////////////////////////////////////////////////////////////////
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);
//////////////////////////////////////////////////////////////////////////////
// Internal - Do not call //
//////////////////////////////////////////////////////////////////////////////
public static native int 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,47 @@
package com.zerotier.libzt;
import com.zerotier.libzt.ZeroTier;
import com.zerotier.libzt.ZeroTierSocketAddress;
public class ZeroTierPeerDetails
{
/**
* ZeroTier address (40 bits)
*/
public long address;
/**
* Remote major version or -1 if not known
*/
public int versionMajor;
/**
* Remote minor version or -1 if not known
*/
public int versionMinor;
/**
* Remote revision or -1 if not known
*/
public int versionRev;
/**
* Last measured latency in milliseconds or -1 if unknown
*/
public int latency;
/**
* What trust hierarchy role does this device have?
*/
public int role;
/**
* Number of paths (size of paths[])
*/
public int pathCount;
/**
* Known network paths to peer
*/
public ZeroTierSocketAddress[] paths = new ZeroTierSocketAddress[ZeroTier.ZT_MAX_PEER_NETWORK_PATHS];
}

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,748 @@
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(ex.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,771 @@
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;
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
}