This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
zhangyang-libzt/src/bindings/java/ZeroTierSocket.java

669 lines
22 KiB
Java
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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");
}
}
}