Moved Java sources to src/java
This commit is contained in:
241
src/java/ZeroTier.java
Normal file
241
src/java/ZeroTier.java
Normal 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
|
||||
}
|
||||
9
src/java/ZeroTierEventListener.java
Normal file
9
src/java/ZeroTierEventListener.java
Normal 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);
|
||||
}
|
||||
28
src/java/ZeroTierFileDescriptorSet.java
Normal file
28
src/java/ZeroTierFileDescriptorSet.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
209
src/java/ZeroTierInputStream.java
Normal file
209
src/java/ZeroTierInputStream.java
Normal 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
|
||||
}
|
||||
}
|
||||
6
src/java/ZeroTierIoctlArg.java
Normal file
6
src/java/ZeroTierIoctlArg.java
Normal 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
|
||||
}
|
||||
81
src/java/ZeroTierOutputStream.java
Normal file
81
src/java/ZeroTierOutputStream.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/java/ZeroTierPeerDetails.java
Normal file
47
src/java/ZeroTierPeerDetails.java
Normal 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];
|
||||
}
|
||||
101
src/java/ZeroTierSSLSocketFactory.java
Normal file
101
src/java/ZeroTierSSLSocketFactory.java
Normal 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];
|
||||
}
|
||||
}
|
||||
748
src/java/ZeroTierSocket.java
Normal file
748
src/java/ZeroTierSocket.java
Normal 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();
|
||||
}
|
||||
*/
|
||||
}
|
||||
66
src/java/ZeroTierSocketAddress.java
Normal file
66
src/java/ZeroTierSocketAddress.java
Normal 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; }
|
||||
}
|
||||
51
src/java/ZeroTierSocketFactory.java
Normal file
51
src/java/ZeroTierSocketFactory.java
Normal 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);
|
||||
}
|
||||
}
|
||||
771
src/java/ZeroTierSocketImpl.java
Normal file
771
src/java/ZeroTierSocketImpl.java
Normal 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();
|
||||
}
|
||||
*/
|
||||
}
|
||||
29
src/java/ZeroTierSocketImplFactory.java
Normal file
29
src/java/ZeroTierSocketImplFactory.java
Normal 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();
|
||||
}
|
||||
}
|
||||
14
src/java/ZeroTierSocketOptionValue.java
Normal file
14
src/java/ZeroTierSocketOptionValue.java
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user