package io.netty.channel.unix;
import io.netty.channel.ChannelException;
import io.netty.util.CharsetUtil;
import io.netty.util.NetUtil;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.PortUnreachableException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.atomic.AtomicBoolean;
import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
import static io.netty.channel.unix.Errors.ERROR_ECONNREFUSED_NEGATIVE;
import static io.netty.channel.unix.Errors.ERRNO_EINPROGRESS_NEGATIVE;
import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE;
import static io.netty.channel.unix.Errors.ioResult;
import static io.netty.channel.unix.Errors.newIOException;
import static io.netty.channel.unix.Errors.throwConnectException;
import static io.netty.channel.unix.LimitsStaticallyReferencedJniMethods.udsSunPathSize;
import static io.netty.channel.unix.NativeInetAddress.address;
import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
import static io.netty.util.internal.ThrowableUtil.unknownStackTrace;
public class Socket extends FileDescriptor {
private static final ClosedChannelException SHUTDOWN_CLOSED_CHANNEL_EXCEPTION = unknownStackTrace(
new ClosedChannelException(), Socket.class, "shutdown(..)");
private static final ClosedChannelException SEND_TO_CLOSED_CHANNEL_EXCEPTION = unknownStackTrace(
new ClosedChannelException(), Socket.class, "sendTo(..)");
private static final ClosedChannelException SEND_TO_ADDRESS_CLOSED_CHANNEL_EXCEPTION =
unknownStackTrace(new ClosedChannelException(), Socket.class, "sendToAddress(..)");
private static final ClosedChannelException SEND_TO_ADDRESSES_CLOSED_CHANNEL_EXCEPTION =
unknownStackTrace(new ClosedChannelException(), Socket.class, "sendToAddresses(..)");
private static final Errors.NativeIoException SEND_TO_CONNECTION_RESET_EXCEPTION = unknownStackTrace(
Errors.newConnectionResetException("syscall:sendto", Errors.ERRNO_EPIPE_NEGATIVE),
Socket.class, "sendTo(..)");
private static final Errors.NativeIoException SEND_TO_ADDRESS_CONNECTION_RESET_EXCEPTION =
unknownStackTrace(Errors.newConnectionResetException("syscall:sendto",
Errors.ERRNO_EPIPE_NEGATIVE), Socket.class, "sendToAddress");
private static final Errors.NativeIoException CONNECTION_RESET_EXCEPTION_SENDMSG = unknownStackTrace(
Errors.newConnectionResetException("syscall:sendmsg",
Errors.ERRNO_EPIPE_NEGATIVE), Socket.class, "sendToAddresses(..)");
private static final Errors.NativeIoException CONNECTION_RESET_SHUTDOWN_EXCEPTION =
unknownStackTrace(Errors.newConnectionResetException("syscall:shutdown",
Errors.ERRNO_ECONNRESET_NEGATIVE), Socket.class, "shutdown");
private static final Errors.NativeConnectException FINISH_CONNECT_REFUSED_EXCEPTION =
unknownStackTrace(new Errors.NativeConnectException("syscall:getsockopt",
Errors.ERROR_ECONNREFUSED_NEGATIVE), Socket.class, "finishConnect(..)");
private static final Errors.NativeConnectException CONNECT_REFUSED_EXCEPTION =
unknownStackTrace(new Errors.NativeConnectException("syscall:connect",
Errors.ERROR_ECONNREFUSED_NEGATIVE), Socket.class, "connect(..)");
public static final int UDS_SUN_PATH_SIZE = udsSunPathSize();
public Socket(int fd) {
super(fd);
}
public final void shutdown() throws IOException {
shutdown(true, true);
}
public final void shutdown(boolean read, boolean write) throws IOException {
for (;;) {
final int oldState = this.state;
if (isClosed(oldState)) {
throw new ClosedChannelException();
}
int newState = oldState;
if (read && !isInputShutdown(newState)) {
newState = inputShutdown(newState);
}
if (write && !isOutputShutdown(newState)) {
newState = outputShutdown(newState);
}
if (newState == oldState) {
return;
}
if (casState(oldState, newState)) {
break;
}
}
int res = shutdown(fd, read, write);
if (res < 0) {
ioResult("shutdown", res, CONNECTION_RESET_SHUTDOWN_EXCEPTION, SHUTDOWN_CLOSED_CHANNEL_EXCEPTION);
}
}
public final boolean isShutdown() {
int state = this.state;
return isInputShutdown(state) && isOutputShutdown(state);
}
public final boolean isInputShutdown() {
return isInputShutdown(state);
}
public final boolean isOutputShutdown() {
return isOutputShutdown(state);
}
public final int sendTo(ByteBuffer buf, int pos, int limit, InetAddress addr, int port) throws IOException {
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendTo(fd, buf, pos, limit, address, scopeId, port);
if (res >= 0) {
return res;
}
if (res == ERROR_ECONNREFUSED_NEGATIVE) {
throw new PortUnreachableException("sendTo failed");
}
return ioResult("sendTo", res, SEND_TO_CONNECTION_RESET_EXCEPTION, SEND_TO_CLOSED_CHANNEL_EXCEPTION);
}
public final int sendToAddress(long memoryAddress, int pos, int limit, InetAddress addr, int port)
throws IOException {
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendToAddress(fd, memoryAddress, pos, limit, address, scopeId, port);
if (res >= 0) {
return res;
}
if (res == ERROR_ECONNREFUSED_NEGATIVE) {
throw new PortUnreachableException("sendToAddress failed");
}
return ioResult("sendToAddress", res,
SEND_TO_ADDRESS_CONNECTION_RESET_EXCEPTION, SEND_TO_ADDRESS_CLOSED_CHANNEL_EXCEPTION);
}
public final int sendToAddresses(long memoryAddress, int length, InetAddress addr, int port) throws IOException {
byte[] address;
int scopeId;
if (addr instanceof Inet6Address) {
address = addr.getAddress();
scopeId = ((Inet6Address) addr).getScopeId();
} else {
scopeId = 0;
address = ipv4MappedIpv6Address(addr.getAddress());
}
int res = sendToAddresses(fd, memoryAddress, length, address, scopeId, port);
if (res >= 0) {
return res;
}
if (res == ERROR_ECONNREFUSED_NEGATIVE) {
throw new PortUnreachableException("sendToAddresses failed");
}
return ioResult("sendToAddresses", res,
CONNECTION_RESET_EXCEPTION_SENDMSG, SEND_TO_ADDRESSES_CLOSED_CHANNEL_EXCEPTION);
}
public final DatagramSocketAddress recvFrom(ByteBuffer buf, int pos, int limit) throws IOException {
return recvFrom(fd, buf, pos, limit);
}
public final DatagramSocketAddress recvFromAddress(long memoryAddress, int pos, int limit) throws IOException {
return recvFromAddress(fd, memoryAddress, pos, limit);
}
public final int recvFd() throws IOException {
int res = recvFd(fd);
if (res > 0) {
return res;
}
if (res == 0) {
return -1;
}
if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
return 0;
}
throw newIOException("recvFd", res);
}
public final int sendFd(int fdToSend) throws IOException {
int res = sendFd(fd, fdToSend);
if (res >= 0) {
return res;
}
if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
return -1;
}
throw newIOException("sendFd", res);
}
public final boolean connect(SocketAddress socketAddress) throws IOException {
int res;
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
NativeInetAddress address = NativeInetAddress.newInstance(inetSocketAddress.getAddress());
res = connect(fd, address.address, address.scopeId, inetSocketAddress.getPort());
} else if (socketAddress instanceof DomainSocketAddress) {
DomainSocketAddress unixDomainSocketAddress = (DomainSocketAddress) socketAddress;
res = connectDomainSocket(fd, unixDomainSocketAddress.path().getBytes(CharsetUtil.UTF_8));
} else {
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
}
if (res < 0) {
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
return false;
}
throwConnectException("connect", CONNECT_REFUSED_EXCEPTION, res);
}
return true;
}
public final boolean finishConnect() throws IOException {
int res = finishConnect(fd);
if (res < 0) {
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
return false;
}
throwConnectException("finishConnect", FINISH_CONNECT_REFUSED_EXCEPTION, res);
}
return true;
}
public final void disconnect() throws IOException {
int res = disconnect(fd);
if (res < 0) {
throwConnectException("disconnect", FINISH_CONNECT_REFUSED_EXCEPTION, res);
}
}
public final void bind(SocketAddress socketAddress) throws IOException {
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress addr = (InetSocketAddress) socketAddress;
NativeInetAddress address = NativeInetAddress.newInstance(addr.getAddress());
int res = bind(fd, address.address, address.scopeId, addr.getPort());
if (res < 0) {
throw newIOException("bind", res);
}
} else if (socketAddress instanceof DomainSocketAddress) {
DomainSocketAddress addr = (DomainSocketAddress) socketAddress;
int res = bindDomainSocket(fd, addr.path().getBytes(CharsetUtil.UTF_8));
if (res < 0) {
throw newIOException("bind", res);
}
} else {
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
}
}
public final void listen(int backlog) throws IOException {
int res = listen(fd, backlog);
if (res < 0) {
throw newIOException("listen", res);
}
}
public final int accept(byte[] addr) throws IOException {
int res = accept(fd, addr);
if (res >= 0) {
return res;
}
if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
return -1;
}
throw newIOException("accept", res);
}
public final InetSocketAddress remoteAddress() {
byte[] addr = remoteAddress(fd);
return addr == null ? null : address(addr, 0, addr.length);
}
public final InetSocketAddress localAddress() {
byte[] addr = localAddress(fd);
return addr == null ? null : address(addr, 0, addr.length);
}
public final int getReceiveBufferSize() throws IOException {
return getReceiveBufferSize(fd);
}
public final int getSendBufferSize() throws IOException {
return getSendBufferSize(fd);
}
public final boolean isKeepAlive() throws IOException {
return isKeepAlive(fd) != 0;
}
public final boolean isTcpNoDelay() throws IOException {
return isTcpNoDelay(fd) != 0;
}
public final boolean isReuseAddress() throws IOException {
return isReuseAddress(fd) != 0;
}
public final boolean isReusePort() throws IOException {
return isReusePort(fd) != 0;
}
public final boolean isBroadcast() throws IOException {
return isBroadcast(fd) != 0;
}
public final int getSoLinger() throws IOException {
return getSoLinger(fd);
}
public final int getSoError() throws IOException {
return getSoError(fd);
}
public final int getTrafficClass() throws IOException {
return getTrafficClass(fd);
}
public final void setKeepAlive(boolean keepAlive) throws IOException {
setKeepAlive(fd, keepAlive ? 1 : 0);
}
public final void setReceiveBufferSize(int receiveBufferSize) throws IOException {
setReceiveBufferSize(fd, receiveBufferSize);
}
public final void setSendBufferSize(int sendBufferSize) throws IOException {
setSendBufferSize(fd, sendBufferSize);
}
public final void setTcpNoDelay(boolean tcpNoDelay) throws IOException {
setTcpNoDelay(fd, tcpNoDelay ? 1 : 0);
}
public final void setSoLinger(int soLinger) throws IOException {
setSoLinger(fd, soLinger);
}
public final void setReuseAddress(boolean reuseAddress) throws IOException {
setReuseAddress(fd, reuseAddress ? 1 : 0);
}
public final void setReusePort(boolean reusePort) throws IOException {
setReusePort(fd, reusePort ? 1 : 0);
}
public final void setBroadcast(boolean broadcast) throws IOException {
setBroadcast(fd, broadcast ? 1 : 0);
}
public final void setTrafficClass(int trafficClass) throws IOException {
setTrafficClass(fd, trafficClass);
}
@Override
public String toString() {
return "Socket{" +
"fd=" + fd +
'}';
}
private static final AtomicBoolean INITIALIZED = new AtomicBoolean();
public static Socket newSocketStream() {
return new Socket(newSocketStream0());
}
public static Socket newSocketDgram() {
return new Socket(newSocketDgram0());
}
public static Socket newSocketDomain() {
return new Socket(newSocketDomain0());
}
public static void initialize() {
if (INITIALIZED.compareAndSet(false, true)) {
initialize(NetUtil.isIpV4StackPreferred());
}
}
protected static int newSocketStream0() {
int res = newSocketStreamFd();
if (res < 0) {
throw new ChannelException(newIOException("newSocketStream", res));
}
return res;
}
protected static int newSocketDgram0() {
int res = newSocketDgramFd();
if (res < 0) {
throw new ChannelException(newIOException("newSocketDgram", res));
}
return res;
}
protected static int newSocketDomain0() {
int res = newSocketDomainFd();
if (res < 0) {
throw new ChannelException(newIOException("newSocketDomain", res));
}
return res;
}
private static native int shutdown(int fd, boolean read, boolean write);
private static native int connect(int fd, byte[] address, int scopeId, int port);
private static native int connectDomainSocket(int fd, byte[] path);
private static native int finishConnect(int fd);
private static native int disconnect(int fd);
private static native int bind(int fd, byte[] address, int scopeId, int port);
private static native int bindDomainSocket(int fd, byte[] path);
private static native int listen(int fd, int backlog);
private static native int accept(int fd, byte[] addr);
private static native byte[] remoteAddress(int fd);
private static native byte[] localAddress(int fd);
private static native int sendTo(
int fd, ByteBuffer buf, int pos, int limit, byte[] address, int scopeId, int port);
private static native int sendToAddress(
int fd, long memoryAddress, int pos, int limit, byte[] address, int scopeId, int port);
private static native int sendToAddresses(
int fd, long memoryAddress, int length, byte[] address, int scopeId, int port);
private static native DatagramSocketAddress recvFrom(
int fd, ByteBuffer buf, int pos, int limit) throws IOException;
private static native DatagramSocketAddress recvFromAddress(
int fd, long memoryAddress, int pos, int limit) throws IOException;
private static native int recvFd(int fd);
private static native int sendFd(int socketFd, int fd);
private static native int newSocketStreamFd();
private static native int newSocketDgramFd();
private static native int newSocketDomainFd();
private static native int isReuseAddress(int fd) throws IOException;
private static native int isReusePort(int fd) throws IOException;
private static native int getReceiveBufferSize(int fd) throws IOException;
private static native int getSendBufferSize(int fd) throws IOException;
private static native int isKeepAlive(int fd) throws IOException;
private static native int isTcpNoDelay(int fd) throws IOException;
private static native int isBroadcast(int fd) throws IOException;
private static native int getSoLinger(int fd) throws IOException;
private static native int getSoError(int fd) throws IOException;
private static native int getTrafficClass(int fd) throws IOException;
private static native void setReuseAddress(int fd, int reuseAddress) throws IOException;
private static native void setReusePort(int fd, int reuseAddress) throws IOException;
private static native void setKeepAlive(int fd, int keepAlive) throws IOException;
private static native void setReceiveBufferSize(int fd, int receiveBufferSize) throws IOException;
private static native void setSendBufferSize(int fd, int sendBufferSize) throws IOException;
private static native void setTcpNoDelay(int fd, int tcpNoDelay) throws IOException;
private static native void setSoLinger(int fd, int soLinger) throws IOException;
private static native void setBroadcast(int fd, int broadcast) throws IOException;
private static native void setTrafficClass(int fd, int trafficClass) throws IOException;
private static native void initialize(boolean ipv4Preferred);
}