diff options
| author | Brenton Bostick <[email protected]> | 2022-12-09 06:40:06 -0500 |
|---|---|---|
| committer | Brenton Bostick <[email protected]> | 2023-05-22 15:36:21 -0400 |
| commit | 48adf41cc1ca35e82d81f510a1910e82e222e264 (patch) | |
| tree | fa37703386129e941401fbb106b93229d2a4aed4 /src/bindings/java/com/zerotier/sockets/ZeroTierSocket.java | |
| parent | 71c2f97056b61a1d030d40d4e33e49a80a7f0ae6 (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.java | 668 |
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"); + } + } +} |
