14 changed files with 2401 additions and 0 deletions
@ -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
|
||||
} |
||||
@ -0,0 +1,9 @@
|
||||
package com.zerotier.libzt; |
||||
|
||||
public interface ZeroTierEventListener { |
||||
|
||||
/* |
||||
* Called when an even occurs in the native section of the ZeroTier library service |
||||
*/ |
||||
public void onZeroTierEvent(long nwid, int eventCode); |
||||
} |
||||
@ -0,0 +1,28 @@
|
||||
package com.zerotier.libzt; |
||||
|
||||
public class ZeroTierFileDescriptorSet |
||||
{ |
||||
byte[] fds_bits = new byte[1024]; |
||||
|
||||
public void CLR(int fd) |
||||
{ |
||||
fds_bits[fd] = 0x00; |
||||
} |
||||
|
||||
public boolean ISSET(int fd) |
||||
{ |
||||
return fds_bits[fd] == 0x01; |
||||
} |
||||
|
||||
public void SET(int fd) |
||||
{ |
||||
fds_bits[fd] = 0x01; |
||||
} |
||||
|
||||
public void ZERO() |
||||
{ |
||||
for (int i=0; i<1024; i++) { |
||||
fds_bits[i] = 0x00; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@
|
||||
package com.zerotier.libzt; |
||||
|
||||
public class ZeroTierIoctlArg |
||||
{ |
||||
int integer; // General integer to be used or updated by the zts_ioctl() call
|
||||
} |
||||
@ -0,0 +1,81 @@
|
||||
package com.zerotier.libzt; |
||||
|
||||
import com.zerotier.libzt.ZeroTier; |
||||
|
||||
import java.io.*; |
||||
import java.util.Arrays; |
||||
import java.util.Objects; |
||||
|
||||
class ZeroTierOutputStream extends OutputStream |
||||
{ |
||||
/* |
||||
* File descriptor used by lower native layer |
||||
*/ |
||||
int zfd; |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public void flush() |
||||
throws IOException |
||||
{ |
||||
// System.err.println("flush: ZeroTierOutputStream does not currently support this feature");
|
||||
// Not fully supported since we don't maintain a buffer
|
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public void close() |
||||
throws IOException |
||||
{ |
||||
/* Note: this operation currently only stops RX on a socket that is shared |
||||
between both I/OStreams. This means that closing this stream will only shutdown |
||||
that aspect of the socket but not actually close it and free resources. Resources |
||||
will be properly freed when the socket implementation's close() is called or if |
||||
both I/OStreams are closed separately */ |
||||
ZeroTier.shutdown(zfd, ZeroTier.SHUT_WR); |
||||
zfd = -1; |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public void write(byte[] b) |
||||
throws IOException |
||||
{ |
||||
int err = ZeroTier.write(zfd, b); |
||||
if (err < 0) { |
||||
throw new IOException("write(b[]), errno="+err); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public void write(byte[] b, int off, int len) |
||||
throws IOException |
||||
{ |
||||
Objects.requireNonNull(b, "input byte array must not be null"); |
||||
if ((off < 0) | (len < 0) | (off+len > b.length)) { |
||||
throw new IndexOutOfBoundsException("write(b,off,len)"); |
||||
} |
||||
int err = ZeroTier.write_offset(zfd, b, off, len); |
||||
if (err < 0) { |
||||
throw new IOException("write(b[],off,len), errno="+err); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public void write(int b) |
||||
throws IOException |
||||
{ |
||||
byte lowByte = (byte)(b & 0xFF); |
||||
int err = ZeroTier.write_byte(zfd, lowByte); |
||||
if (err < 0) { |
||||
throw new IOException("write(b), errno="+err); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,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]; |
||||
} |
||||
@ -0,0 +1,101 @@
|
||||
package com.zerotier.libzt; |
||||
|
||||
import com.zerotier.libzt.ZeroTierSocket; |
||||
|
||||
import java.net.*; |
||||
import javax.net.SocketFactory; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.security.*; |
||||
import java.util.Locale; |
||||
|
||||
import javax.net.ssl.SSLSocketFactory; |
||||
|
||||
public class ZeroTierSSLSocketFactory extends SSLSocketFactory |
||||
{ |
||||
private final SSLSocketFactory delegate; |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public ZeroTierSSLSocketFactory(SSLSocketFactory delegate) |
||||
{ |
||||
this.delegate = delegate; |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public Socket createSocket(Socket s, String host, int port, boolean autoClose) |
||||
throws IOException |
||||
{ |
||||
ZeroTierSocket zs = new ZeroTierSocket(); |
||||
zs.connect((SocketAddress)new InetSocketAddress(host, port), 10); |
||||
return delegate.createSocket(zs, host, port, autoClose); |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public Socket createSocket(Socket s, InputStream consumed, boolean autoClose) |
||||
throws IOException |
||||
{ |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public Socket createSocket(InetAddress a,int b,InetAddress c,int d) |
||||
throws IOException |
||||
{ |
||||
ZeroTierSocket s = new ZeroTierSocket(); |
||||
return delegate.createSocket(a, b, c, d); |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public Socket createSocket(InetAddress a,int b) |
||||
throws IOException |
||||
{ |
||||
ZeroTierSocket s = new ZeroTierSocket(); |
||||
return delegate.createSocket(a, b); |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public Socket createSocket(String a,int b,InetAddress c,int d) |
||||
throws IOException |
||||
{ |
||||
ZeroTierSocket s = new ZeroTierSocket(); |
||||
return delegate.createSocket(a, b, c, d); |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public Socket createSocket(String a,int b) |
||||
throws IOException |
||||
{ |
||||
ZeroTierSocket s = new ZeroTierSocket(); |
||||
return delegate.createSocket(a, b); |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public String [] getSupportedCipherSuites() |
||||
{ |
||||
return new String[0]; |
||||
} |
||||
|
||||
/* |
||||
* |
||||
*/ |
||||
public String [] getDefaultCipherSuites() |
||||
{ |
||||
return new String[0]; |
||||
} |
||||
} |
||||
@ -0,0 +1,66 @@
|
||||
package com.zerotier.libzt; |
||||
|
||||
import com.zerotier.libzt.ZeroTier; |
||||
|
||||
import java.net.InetAddress; |
||||
|
||||
// Designed to transport address information across the JNI boundary
|
||||
public class ZeroTierSocketAddress |
||||
{ |
||||
public byte[] _ip6 = new byte[16]; |
||||
public byte[] _ip4 = new byte[4]; |
||||
|
||||
public int _family; |
||||
public int _port; // Also reused for netmask or prefix
|
||||
|
||||
public ZeroTierSocketAddress() {} |
||||
|
||||
public ZeroTierSocketAddress(String ipStr, int port) |
||||
{ |
||||
if(ipStr.contains(":")) { |
||||
_family = ZeroTier.AF_INET6; |
||||
try { |
||||
InetAddress ip = InetAddress.getByName(ipStr); |
||||
_ip6 = ip.getAddress(); |
||||
} |
||||
catch (Exception e) { } |
||||
} |
||||
else if(ipStr.contains(".")) { |
||||
_family = ZeroTier.AF_INET; |
||||
try { |
||||
InetAddress ip = InetAddress.getByName(ipStr); |
||||
_ip4 = ip.getAddress(); |
||||
} |
||||
catch (Exception e) { } |
||||
} |
||||
_port = port; |
||||
} |
||||
|
||||
public int getPort() { return _port; } |
||||
public int getNetmask() { return _port; } |
||||
public int getPrefix() { return _port; } |
||||
|
||||
public String ipString() |
||||
{ |
||||
if (_family == ZeroTier.AF_INET) { |
||||
try { |
||||
InetAddress inet = InetAddress.getByAddress(_ip4); |
||||
return "" + inet.getHostAddress(); |
||||
} catch (Exception e) { |
||||
System.out.println(e); |
||||
} |
||||
} |
||||
if (_family == ZeroTier.AF_INET6) { |
||||
try { |
||||
InetAddress inet = InetAddress.getByAddress(_ip6); |
||||
return "" + inet.getHostAddress(); |
||||
} catch (Exception e) { |
||||
System.out.println(e); |
||||
} |
||||
} |
||||
return ""; |
||||
} |
||||
|
||||
public String toString() { return ipString() + ":" + _port; } |
||||
public String toCIDR() { return ipString() + "/" + _port; } |
||||
} |
||||
@ -0,0 +1,51 @@
|
||||
package com.zerotier.libzt; |
||||
|
||||
import com.zerotier.libzt.ZeroTier; |
||||
import com.zerotier.libzt.ZeroTierSocket; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.InetAddress; |
||||
import java.net.Socket; |
||||
import java.net.UnknownHostException; |
||||
import javax.net.SocketFactory; |
||||
import java.util.Objects; |
||||
|
||||
public class ZeroTierSocketFactory extends SocketFactory |
||||
{ |
||||
public ZeroTierSocketFactory() { } |
||||
|
||||
public static SocketFactory getDefault() |
||||
{ |
||||
return null; |
||||
} |
||||
|
||||
public Socket createSocket() |
||||
throws IOException, UnknownHostException |
||||
{ |
||||
return new ZeroTierSocket(); |
||||
} |
||||
|
||||
public Socket createSocket(String host, int port) |
||||
throws IOException, UnknownHostException |
||||
{ |
||||
return new ZeroTierSocket(host, port); |
||||
} |
||||
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) |
||||
throws IOException, UnknownHostException |
||||
{ |
||||
return new ZeroTierSocket(host, port, localHost, localPort); |
||||
} |
||||
|
||||
public Socket createSocket(InetAddress host, int port) |
||||
throws IOException |
||||
{ |
||||
return new ZeroTierSocket(host, port); |
||||
} |
||||
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) |
||||
throws IOException |
||||
{ |
||||
return new ZeroTierSocket(address, port, localAddress, localPort); |
||||
} |
||||
} |
||||
@ -0,0 +1,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(); |
||||
} |
||||
*/ |
||||
} |
||||
@ -0,0 +1,29 @@
|
||||
package com.zerotier.libzt; |
||||
|
||||
import com.zerotier.libzt.ZeroTier; |
||||
import com.zerotier.libzt.ZeroTierSocketImpl; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.InetAddress; |
||||
import java.net.Socket; |
||||
import java.net.UnknownHostException; |
||||
import javax.net.SocketFactory; |
||||
import java.net.SocketImplFactory; |
||||
import java.util.Objects; |
||||
|
||||
|
||||
public class ZeroTierSocketImplFactory implements SocketImplFactory |
||||
{ |
||||
/* |
||||
* Does nothing |
||||
*/ |
||||
public ZeroTierSocketImplFactory() { } |
||||
|
||||
/* |
||||
* Produces a ZeroTierSocketImpl |
||||
*/ |
||||
public ZeroTierSocketImpl createSocketImpl() |
||||
{ |
||||
return new ZeroTierSocketImpl(); |
||||
} |
||||
} |
||||
@ -0,0 +1,14 @@
|
||||
package com.zerotier.libzt; |
||||
|
||||
public class ZeroTierSocketOptionValue |
||||
{ |
||||
public boolean isBoolean = false; |
||||
public boolean booleanValue; |
||||
|
||||
public boolean isInteger = false; |
||||
public int integerValue; |
||||
|
||||
public boolean isTimeval = false; |
||||
public int tv_sec; // seconds
|
||||
public int tv_usec; // microseconds
|
||||
} |
||||
Loading…
Reference in new issue