summaryrefslogtreecommitdiff
path: root/src/bindings/java/com/zerotier/sockets/ZeroTierSocket.java
diff options
context:
space:
mode:
authorBrenton Bostick <[email protected]>2022-12-09 06:40:06 -0500
committerBrenton Bostick <[email protected]>2023-05-22 15:36:21 -0400
commit48adf41cc1ca35e82d81f510a1910e82e222e264 (patch)
treefa37703386129e941401fbb106b93229d2a4aed4 /src/bindings/java/com/zerotier/sockets/ZeroTierSocket.java
parent71c2f97056b61a1d030d40d4e33e49a80a7f0ae6 (diff)
Move java files to com/zerotier/sockets subfolder
This adheres to standard Java style and fixes: Package name 'com.zerotier.sockets' does not correspond to the file path '' in Android Studio
Diffstat (limited to 'src/bindings/java/com/zerotier/sockets/ZeroTierSocket.java')
-rw-r--r--src/bindings/java/com/zerotier/sockets/ZeroTierSocket.java668
1 files changed, 668 insertions, 0 deletions
diff --git a/src/bindings/java/com/zerotier/sockets/ZeroTierSocket.java b/src/bindings/java/com/zerotier/sockets/ZeroTierSocket.java
new file mode 100644
index 0000000..ffedb3b
--- /dev/null
+++ b/src/bindings/java/com/zerotier/sockets/ZeroTierSocket.java
@@ -0,0 +1,668 @@
+/*
+ * Copyright (c)2013-2021 ZeroTier, Inc.
+ *
+ * Use of this software is governed by the Business Source License included
+ * in the LICENSE.TXT file in the project's root directory.
+ *
+ * Change Date: 2026-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by version 2.0 of the Apache License.
+ */
+/****/
+
+package com.zerotier.sockets;
+
+import com.zerotier.sockets.*;
+import java.io.*;
+import java.net.*;
+
+/**
+ * Implements Socket-like behavior over ZeroTier
+ *
+ * @author ZeroTier, Inc.
+ */
+public class ZeroTierSocket {
+ // File descriptor from lower native layer
+ private int _zfd = -1;
+ private int _family = -1;
+ private int _type = -1;
+ private int _protocol = -1;
+
+ // State flags
+ private boolean _isClosed = false;
+ private boolean _isConnected = false;
+ private boolean _isBound = false;
+ private boolean _inputHasBeenShutdown = false;
+ private boolean _outputHasBeenShutdown = false;
+
+ // Input and Output streams
+ private ZeroTierInputStream _inputStream = new ZeroTierInputStream();
+ private ZeroTierOutputStream _outputStream = new ZeroTierOutputStream();
+
+ // The remote address to which the ZeroTierSocket is connected
+ private InetAddress _remoteAddr;
+ private int _remotePort;
+ private InetAddress _localAddr;
+ private int _localPort;
+
+ private void setNativeFileDescriptor(int fd)
+ {
+ _zfd = fd;
+ _inputStream.zfd = fd;
+ _outputStream.zfd = fd;
+ }
+
+ public int getNativeFileDescriptor()
+ {
+ return _zfd;
+ }
+
+ private ZeroTierSocket(int family, int type, int protocol, int zfd)
+ {
+ _family = family;
+ _type = type;
+ _protocol = protocol;
+ setNativeFileDescriptor(zfd);
+ // Since we only call this from accept() we will mark it as connected
+ _isConnected = true;
+ }
+
+ public ZeroTierSocket(String remoteAddr, int remotePort) throws IOException
+ {
+ _protocol = 0;
+ _type = ZeroTierNative.ZTS_SOCK_STREAM;
+
+ InetAddress address = InetAddress.getByName(remoteAddr);
+ if (address instanceof Inet6Address) {
+ _family = ZeroTierNative.ZTS_AF_INET6;
+ }
+ else if (address instanceof Inet4Address) {
+ _family = ZeroTierNative.ZTS_AF_INET;
+ }
+
+ _zfd = ZeroTierNative.zts_bsd_socket(_family, _type, _protocol);
+ setNativeFileDescriptor(_zfd);
+ int err;
+ if ((err = ZeroTierNative.zts_connect(_zfd, remoteAddr, remotePort, 0)) < 0) {
+ throw new IOException("Error while connecting to remote host (" + err + ")");
+ }
+ _isConnected = true;
+ }
+
+ /**
+ * Create a new ZeroTierSocket with the given attributes
+ * @param family The socket family
+ * @param type The socket type
+ * @param protocol Supported protocol
+ *
+ * @exception IOException when an I/O error occurs
+ */
+ public ZeroTierSocket(int family, int type, int protocol) throws IOException
+ {
+ if (_zfd > -1) {
+ throw new IOException("This socket has already been created (fd=" + _zfd + ")");
+ }
+ _zfd = ZeroTierNative.zts_bsd_socket(family, type, protocol);
+ if (_zfd < 0) {
+ throw new IOException("Error while creating socket (" + _zfd + ")");
+ }
+ _family = family;
+ _type = type;
+ _protocol = protocol;
+ setNativeFileDescriptor(_zfd);
+ }
+
+ /**
+ * Connect to a remote host
+ * @param remoteAddr Remote address to which this socket should connect
+ * @param remotePort Remote port to which this socket should connect
+ *
+ * @exception IOException when an I/O error occurs
+ */
+ public void connect(InetAddress remoteAddr, int remotePort) throws IOException
+ {
+ if (_zfd < 0) {
+ throw new IOException("Invalid socket (fd < 0)");
+ }
+ if ((remoteAddr instanceof Inet4Address) && _family != ZeroTierNative.ZTS_AF_INET) {
+ throw new IOException("Invalid address type. Socket is of type AF_INET");
+ }
+ if ((remoteAddr instanceof Inet6Address) && _family != ZeroTierNative.ZTS_AF_INET6) {
+ throw new IOException("Invalid address type. Socket is of type AF_INET6");
+ }
+ int err;
+ if ((err = ZeroTierNative.zts_connect(_zfd, remoteAddr.getHostAddress(), remotePort, 0)) < 0) {
+ throw new IOException("Error while connecting to remote host (" + err + ")");
+ }
+ _isConnected = true;
+ }
+
+ /**
+ * Connect to a remote host
+ * @param remoteAddr Remote address to which this socket should connect
+ * @param remotePort Remote port to which this socket should connect
+ *
+ * @exception IOException when an I/O error occurs
+ */
+ public void connect(String remoteAddr, int remotePort) throws IOException
+ {
+ InetAddress remoteInetAddr = InetAddress.getByName(remoteAddr);
+ connect(remoteInetAddr, remotePort);
+ }
+
+ /**
+ * Connect to a remote host
+ * @param remoteAddr Remote address to which this socket should connect
+ *
+ * @exception IOException when an I/O error occurs
+ */
+ public void connect(SocketAddress remoteAddr) throws IOException
+ {
+ int remotePort = ((InetSocketAddress)remoteAddr).getPort();
+ connect(((InetSocketAddress)remoteAddr).getHostString(), remotePort);
+ }
+
+ /**
+ * Bind to a local address
+ * @param localAddr Local address to which this socket should bind
+ * @param localPort Local port to which this socket should bind
+ *
+ * @exception IOException when an I/O error occurs
+ */
+ public void bind(InetAddress localAddr, int localPort) throws IOException
+ {
+ if (_zfd < 0) {
+ throw new IOException("Invalid socket (fd < 0)");
+ }
+ if ((localAddr instanceof Inet4Address) && _family != ZeroTierNative.ZTS_AF_INET) {
+ throw new IOException("Invalid address type. Socket is of type AF_INET");
+ }
+ if ((localAddr instanceof Inet6Address) && _family != ZeroTierNative.ZTS_AF_INET6) {
+ throw new IOException("Invalid address type. Socket is of type AF_INET6");
+ }
+ int err;
+ if ((err = ZeroTierNative.zts_bind(_zfd, localAddr.getHostAddress(), localPort)) < 0) {
+ throw new IOException("Error while connecting to remote host (" + err + ")");
+ }
+ _localPort = localPort;
+ _isBound = true;
+ }
+
+ /**
+ * Bind to a local address
+ * @param localAddr Local address to which this socket should bind
+ * @param localPort Local port to which this socket should bind
+ *
+ * @exception IOException when an I/O error occurs
+ */
+ public void bind(String localAddr, int localPort) throws IOException
+ {
+ InetAddress localInetAddr = InetAddress.getByName(localAddr);
+ bind(localInetAddr, localPort);
+ }
+
+ /**
+ * Put the ZeroTierSocket into a listening state
+ * @param backlog Size of connection backlog
+ *
+ * @exception IOException when an I/O error occurs
+ */
+ public void listen(int backlog) throws IOException
+ {
+ if (_zfd < 0) {
+ throw new IOException("Invalid socket (fd < 0)");
+ }
+ if (backlog < 0) {
+ throw new IOException("Invalid backlog value");
+ }
+ int err;
+ if ((err = ZeroTierNative.zts_bsd_listen(_zfd, backlog)) < 0) {
+ throw new IOException("Error while putting socket into listening state (" + err + ")");
+ }
+ }
+
+ /**
+ * Accept incoming connections on this ZeroTierSocket
+ * @return New ZeroTierSocket representing the accepted connection
+ * @exception IOException when an I/O error occurs
+ */
+ public ZeroTierSocket accept() throws IOException
+ {
+ if (_zfd < 0) {
+ throw new IOException("Invalid socket (fd < 0)");
+ }
+ int accetpedFd = -1;
+ ZeroTierSocketAddress addr = new ZeroTierSocketAddress();
+ if ((accetpedFd = ZeroTierNative.zts_bsd_accept(_zfd, addr)) < 0) {
+ throw new IOException("Error while accepting connection (" + accetpedFd + ")");
+ }
+ return new ZeroTierSocket(_family, _type, _protocol, accetpedFd);
+ }
+
+ /**
+ * Close the ZeroTierSocket.
+ *
+ * @exception IOException when an I/O error occurs
+ */
+ public void close() throws IOException
+ {
+ if (_zfd < 0) {
+ throw new IOException("Invalid socket (fd < 0)");
+ }
+ ZeroTierNative.zts_bsd_close(_zfd);
+ _isClosed = true;
+ }
+
+ /**
+ * Return whether keepalive is enabled.
+ * @return true or false
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public boolean getKeepAlive() throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ return ZeroTierNative.zts_get_keepalive(_zfd) == 1;
+ }
+
+ /**
+ * Get the local port to which this ZeroTierSocket is bound
+ * @return Local port
+ */
+ public int getLocalPort()
+ {
+ if (! _isBound) {
+ return -1;
+ }
+ return _localPort;
+ }
+
+ /**
+ * Get the local address to which this ZeroTierSocket is bound
+ * @return Local address
+ */
+ public InetAddress getLocalAddress()
+ {
+ if (! _isBound) {
+ return null;
+ }
+ return _localAddr;
+ }
+
+ /**
+ * Get the remote port to which this ZeroTierSocket is connected
+ * @return Remote port
+ */
+ public int getRemotePort()
+ {
+ if (! _isConnected) {
+ return -1;
+ }
+ return _remotePort;
+ }
+
+ /**
+ * Get the remote address to which this ZeroTierSocket is connected
+ * @return Remote address
+ */
+ public InetAddress getRemoteAddress()
+ {
+ if (! _isConnected) {
+ return null;
+ }
+ return _remoteAddr;
+ }
+
+ /**
+ * Get the remote address to which this ZeroTierSocket is connected. Same as getRemoteAddress()
+ * @return Remote address
+ */
+ public InetAddress getInetAddress()
+ {
+ if (! _isConnected) {
+ return null;
+ }
+ return _remoteAddr;
+ }
+
+ /**
+ * Get the local endpoint address to which this socket is bound to
+ * @return Local endpoint address
+ */
+ public SocketAddress getLocalSocketAddress()
+ {
+ if (! _isConnected) {
+ return null;
+ }
+ return new InetSocketAddress(_remoteAddr, _remotePort);
+ }
+
+ /**
+ * Return the size of the receive buffer for the ZeroTierSocket's ZeroTierInputStream (SO_RCVBUF)
+ * @return Size of the receive buffer
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public int getReceiveBufferSize() throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ return ZeroTierNative.zts_get_recv_buf_size(_zfd);
+ }
+
+ /**
+ * Return the size of the send buffer for the ZeroTierSocket's ZeroTierOutputStream (SO_SNDBUF)
+ * @return Size of the send buffer
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public int getSendBufferSize() throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ return ZeroTierNative.zts_get_send_buf_size(_zfd);
+ }
+
+ /**
+ * Return whether address reuse is enabled on this ZeroTierSocket (SO_REUSEADDR)
+ * @return true or false
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public boolean getReuseAddress() throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ return ZeroTierNative.zts_get_reuse_addr(_zfd) == 1;
+ }
+
+ /**
+ * Return the amount of time that a ZeroTierSocket will linger after closure (SO_LINGER)
+ * @return Nothing.
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public int getSoLingerTime() throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ return ZeroTierNative.zts_get_linger_value(_zfd);
+ }
+
+ /**
+ * Get the ZeroTierSocket's timeout value (SO_RCVTIMEO)
+ * @return Nothing.
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public int getSoTimeout() throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ return ZeroTierNative.zts_get_recv_timeout(_zfd);
+ }
+
+ /**
+ * Return whether TCP no-delay is enabled (TCP_NODELAY)
+ * @return true or false
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public boolean tcpNoDelayEnabled() throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ return ZeroTierNative.zts_get_no_delay(_zfd) == 1;
+ }
+
+ /**
+ * Return whether this ZeroTierSocket is bound to a local address
+ * @return true or false
+ */
+ public boolean isBound​()
+ {
+ return _isBound;
+ }
+
+ /**
+ * Return whether this ZeroTierSocket has been closed
+ * @return true or false
+ */
+ public boolean isClosed​()
+ {
+ return _isClosed;
+ }
+
+ /**
+ * Return whether this ZeroTierSocket is connected to a remote address
+ * @return true or false
+ */
+ public boolean isConnected​()
+ {
+ return _isConnected;
+ }
+
+ /**
+ * Disable the input-aspect of the ZeroTierSocket.
+ *
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public void shutdownInput() throws SocketException
+ {
+ if (! _isConnected) {
+ throw new SocketException("Error: ZeroTierSocket is not connected");
+ }
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (_inputHasBeenShutdown) {
+ throw new SocketException("Error: ZeroTierSocket input has been shut down");
+ }
+ ZeroTierNative.zts_bsd_shutdown(_zfd, ZeroTierNative.ZTS_SHUT_RD);
+ _inputHasBeenShutdown = true;
+ }
+
+ /**
+ * Disable the output-aspect of the ZeroTierSocket.
+ *
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public void shutdownOutput() throws SocketException
+ {
+ if (! _isConnected) {
+ throw new SocketException("Error: ZeroTierSocket is not connected");
+ }
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (_outputHasBeenShutdown) {
+ throw new SocketException("Error: ZeroTierSocket output has been shut down");
+ }
+ ZeroTierNative.zts_bsd_shutdown(_zfd, ZeroTierNative.ZTS_SHUT_WR);
+ _outputHasBeenShutdown = true;
+ }
+
+ /**
+ * Return a reference to the ZeroTierInputStream used by this ZeroTierSocket
+ * @return A reference to the ZeroTierInputStream
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public ZeroTierInputStream getInputStream() throws SocketException
+ {
+ if (! _isConnected) {
+ throw new SocketException("Error: ZeroTierSocket is not connected");
+ }
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (_inputHasBeenShutdown) {
+ throw new SocketException("Error: ZeroTierSocket input has been shut down");
+ }
+ return _inputStream;
+ }
+
+ /**
+ * Return a reference to the ZeroTierOutputStream used by this ZeroTierSocket
+ * @return A reference to the ZeroTierOutputStream
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public ZeroTierOutputStream getOutputStream() throws SocketException
+ {
+ if (! _isConnected) {
+ throw new SocketException("Error: ZeroTierSocket is not connected");
+ }
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (_outputHasBeenShutdown) {
+ throw new SocketException("Error: ZeroTierSocket output has been shut down");
+ }
+ return _outputStream;
+ }
+
+ /**
+ * Return whether the input-aspect of the ZeroTierSocket has been disabled.
+ * @return true or false
+ */
+ public boolean inputStreamHasBeenShutdown​()
+ {
+ return _inputHasBeenShutdown;
+ }
+
+ /**
+ * Return whether the output-aspect of the ZeroTierSocket has been disabled.
+ * @return true or false
+ */
+ public boolean outputStreamHasBeenShutdown​()
+ {
+ return _outputHasBeenShutdown;
+ }
+
+ /**
+ * Enable or disable the keepalive setting (SO_KEEPALIVE)
+ * @param enabled Whether SO_KEEPALIVE is enabled.
+ *
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public void setKeepAliveEnabled(boolean enabled) throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (ZeroTierNative.zts_set_keepalive(_zfd, (enabled ? 1 : 0)) != ZeroTierNative.ZTS_ERR_OK) {
+ throw new SocketException("Error: Could not set SO_KEEPALIVE");
+ }
+ }
+
+ /**
+ * Set the size of the receive buffer for the ZeroTierSocket's ZeroTierInputStream.
+ * @param bufferSize Size of receive buffer
+ *
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public void setReceiveBufferSize(int bufferSize) throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (bufferSize <= 0) {
+ throw new IllegalArgumentException("Error: bufferSize <= 0");
+ }
+ if (ZeroTierNative.zts_set_recv_buf_size(_zfd, bufferSize) != ZeroTierNative.ZTS_ERR_OK) {
+ throw new SocketException("Error: Could not set receive buffer size");
+ }
+ }
+
+ /**
+ * Enable or disable the re-use of addresses (SO_REUSEADDR)
+ * @param enabled Whether SO_REUSEADDR is enabled
+ *
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public void setReuseAddress(boolean enabled) throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (ZeroTierNative.zts_set_reuse_addr(_zfd, (enabled ? 1 : 0)) != ZeroTierNative.ZTS_ERR_OK) {
+ throw new SocketException("Error: Could not set SO_REUSEADDR");
+ }
+ }
+
+ /**
+ * Set the size of the send buffer for the ZeroTierSocket's ZeroTierOutputStream (SO_SNDBUF)
+ * @param bufferSize Size of send buffer
+ *
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public void setSendBufferSize(int bufferSize) throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (bufferSize <= 0) {
+ throw new IllegalArgumentException("Error: bufferSize <= 0");
+ }
+ if (ZeroTierNative.zts_set_send_buf_size(_zfd, bufferSize) != ZeroTierNative.ZTS_ERR_OK) {
+ throw new SocketException("Error: Could not set SO_SNDBUF");
+ }
+ }
+
+ /**
+ * Set the amount of time that a ZeroTierSocket will linger after closure (SO_LINGER)
+ * @param enabled Whether SO_LINGER is enabled
+ * @param lingerTime SO_LINGER time
+ *
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public void setSoLinger(boolean enabled, int lingerTime) throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (lingerTime < 0) {
+ throw new IllegalArgumentException("Error: lingerTime < 0");
+ }
+ if (ZeroTierNative.zts_set_linger(_zfd, (enabled ? 1 : 0), lingerTime) != ZeroTierNative.ZTS_ERR_OK) {
+ throw new SocketException("Error: Could not set ZTS_SO_LINGER");
+ }
+ }
+
+ /**
+ * Set the timeout value for SO_RCVTIMEO
+ * @param timeout Socket receive timeout value.
+ *
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public void setSoTimeout(int timeout) throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (timeout < 0) {
+ throw new IllegalArgumentException("Error: SO_TIMEOUT < 0");
+ }
+ // TODO: This is incorrect
+ if (ZeroTierNative.zts_set_recv_timeout(_zfd, timeout, timeout) != ZeroTierNative.ZTS_ERR_OK) {
+ throw new SocketException("Error: Could not set SO_RCVTIMEO");
+ }
+ }
+
+ /**
+ * Enable or disable TCP_NODELAY
+ * @param enabled Whether TCP_NODELAY is enabled
+ *
+ * @exception SocketException when an error occurs in the native socket layer
+ */
+ public void setTcpNoDelayEnabled(boolean enabled) throws SocketException
+ {
+ if (_isClosed) {
+ throw new SocketException("Error: ZeroTierSocket is closed");
+ }
+ if (ZeroTierNative.zts_set_no_delay(_zfd, (enabled ? 1 : 0)) != ZeroTierNative.ZTS_ERR_OK) {
+ throw new SocketException("Error: Could not set TCP_NODELAY");
+ }
+ }
+}