436 lines
13 KiB
Java
436 lines
13 KiB
Java
|
|
package org.kne.cloud.network;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InterruptedIOException;
|
|
import java.lang.invoke.MethodHandles;
|
|
import java.lang.invoke.VarHandle;
|
|
import java.net.InetAddress;
|
|
import java.net.ProtocolFamily;
|
|
import java.net.DatagramPacket;
|
|
import java.net.DatagramSocket;
|
|
import java.net.Inet4Address;
|
|
import java.net.Inet6Address;
|
|
import java.net.SocketException;
|
|
import java.net.SocketOptions;
|
|
import java.net.StandardProtocolFamily;
|
|
import java.net.UnknownHostException;
|
|
|
|
import org.kne.cloud.network.klalb.KLALBVirtualRawSocketImpl;
|
|
|
|
|
|
public class RawSocket {
|
|
|
|
|
|
public static final ProtocolFamily PF_INET=StandardProtocolFamily.INET;
|
|
|
|
|
|
public static final ProtocolFamily PF_INET6=StandardProtocolFamily.INET6;
|
|
|
|
private static final VarHandle STATE ;
|
|
static {
|
|
try {
|
|
MethodHandles.Lookup l = MethodHandles.lookup();
|
|
STATE = l.findVarHandle(RawSocket.class, "state", int.class);
|
|
} catch (Exception e) {
|
|
throw new InternalError(e);
|
|
}
|
|
}
|
|
//the underlying SocketImpl, may be null, may be swapped when connecting
|
|
private RawSocketImpl impl;
|
|
|
|
// state bits
|
|
private static final int SOCKET_CREATED = 1 << 0; // impl.create(boolean) called
|
|
private static final int BOUND = 1 << 1;
|
|
private static final int CONNECTED = 1 << 2;
|
|
private static final int CLOSED = 1 << 3;
|
|
private static final int SHUT_IN = 1 << 9;
|
|
private static final int SHUT_OUT = 1 << 10;
|
|
private volatile int state;
|
|
|
|
// used to coordinate creating and closing underlying socket
|
|
private final Object socketLock = new Object();
|
|
|
|
|
|
InetAddress connectedAddress = null;
|
|
|
|
private boolean explicitFilter = false;
|
|
private int bytesLeftToFilter;
|
|
/**
|
|
* Atomically sets state to the result of a bitwise OR of the current value
|
|
* and the given mask.
|
|
* @return the previous state value
|
|
*/
|
|
private int getAndBitwiseOrState(int mask) {
|
|
return (int) STATE.getAndBitwiseOr(this, mask);
|
|
}
|
|
|
|
private static boolean isBound(int s) {
|
|
return (s & BOUND) != 0;
|
|
}
|
|
|
|
private static boolean isConnected(int s) {
|
|
return (s & CONNECTED) != 0;
|
|
}
|
|
|
|
private static boolean isClosed(int s) {
|
|
return (s & CLOSED) != 0;
|
|
}
|
|
|
|
private static boolean isInputShutdown(int s) {
|
|
return (s & SHUT_IN) != 0;
|
|
}
|
|
|
|
private static boolean isOutputShutdown(int s) {
|
|
return (s & SHUT_OUT) != 0;
|
|
}
|
|
|
|
|
|
|
|
protected RawSocket(RawSocketImpl impl) {
|
|
this.impl=impl;
|
|
}
|
|
|
|
public RawSocket(RawSocketImpl impl, Inet6Address bindAddress) throws SocketException {
|
|
this.impl=impl;
|
|
bind(bindAddress);
|
|
}
|
|
|
|
public RawSocket(KLALBVirtualRawSocketImpl impl, Inet6Address bindAddress, int bindProtocol) throws SocketException {
|
|
this.impl=impl;
|
|
bind(bindAddress,bindProtocol);
|
|
}
|
|
|
|
public boolean isConnected() {
|
|
return isConnected(state);
|
|
}
|
|
public boolean isBound() {
|
|
return isBound(state);
|
|
}
|
|
public boolean isOpen() {
|
|
return !isClosed(state);
|
|
}
|
|
public boolean isClosed() {
|
|
return isClosed(state);
|
|
}
|
|
private void checkAddress(InetAddress addr, String op) {
|
|
if (addr == null) {
|
|
return;
|
|
}
|
|
if (!(addr instanceof Inet4Address || addr instanceof Inet6Address)) {
|
|
throw new IllegalArgumentException(op + ": invalid address type");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private RawSocketImpl getImpl() throws SocketException {
|
|
if ((state & SOCKET_CREATED) == 0) {
|
|
synchronized (socketLock) {
|
|
int s = state; // re-read state
|
|
if ((s & SOCKET_CREATED) == 0) {
|
|
if (isClosed(s)) {
|
|
throw new SocketException("Socket is closed");
|
|
}
|
|
RawSocketImpl impl = this.impl;
|
|
if (impl == null) {
|
|
this.impl = impl = createImpl();
|
|
}
|
|
try {
|
|
impl.create();
|
|
} catch (SocketException e) {
|
|
throw e;
|
|
} catch (IOException e) {
|
|
throw new SocketException(e.getMessage(), e);
|
|
}
|
|
getAndBitwiseOrState(SOCKET_CREATED);
|
|
}
|
|
}
|
|
}
|
|
return impl;
|
|
}
|
|
|
|
private static RawSocketImpl createImpl() {
|
|
RawSocketImplFactory factory = RawSocket.factory;
|
|
if (factory != null) {
|
|
return factory.createRawSocketImpl();
|
|
} else {
|
|
return null;
|
|
// RawSocketImpl delegate = RawSocketImpl.createPlatformSocketImpl(false);
|
|
// return new SocksSocketImpl(delegate);
|
|
}
|
|
}
|
|
|
|
|
|
private static volatile RawSocketImplFactory factory;
|
|
|
|
static RawSocketImplFactory socketImplFactory() {
|
|
return factory;
|
|
}
|
|
|
|
|
|
@Deprecated(since = "17")
|
|
public static synchronized void setSocketImplFactory(RawSocketImplFactory fac)
|
|
throws IOException
|
|
{
|
|
if (factory != null) {
|
|
throw new SocketException("factory already defined");
|
|
}
|
|
@SuppressWarnings("removal")
|
|
SecurityManager security = System.getSecurityManager();
|
|
if (security != null) {
|
|
security.checkSetFactory();
|
|
}
|
|
factory = fac;
|
|
}
|
|
public void bind(InetAddress address) throws SocketException {
|
|
bind(address,-1);
|
|
}
|
|
public void bind(InetAddress address,int protocolNumber)
|
|
throws SocketException
|
|
{
|
|
int s = state;
|
|
if (isClosed(s))
|
|
throw new SocketException("Socket is closed");
|
|
if (isBound(s))
|
|
throw new SocketException("Already bound");
|
|
|
|
|
|
|
|
if (address == null) {
|
|
try {
|
|
address = Inet6Address.getByName("::0");
|
|
} catch (UnknownHostException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
checkAddress (address, "bind");
|
|
getImpl().bind(address,protocolNumber);
|
|
getAndBitwiseOrState(BOUND);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public void bindDevice(String device)
|
|
throws UnsupportedOperationException, IllegalStateException, IOException
|
|
{
|
|
if(!isOpen())
|
|
throw new IllegalStateException();
|
|
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
|
|
public InetAddress getLocalAddress() {
|
|
if (isClosed())
|
|
return null;
|
|
InetAddress in;
|
|
try {
|
|
in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR);
|
|
if (in.isAnyLocalAddress()) {
|
|
in = InetAddress.getByName("::0");
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
try {
|
|
in = InetAddress.getByName("::0");
|
|
} catch (UnknownHostException e1) {
|
|
e1.printStackTrace();
|
|
in=null;
|
|
}
|
|
}
|
|
return in;
|
|
}
|
|
|
|
|
|
public void close() {
|
|
synchronized (socketLock) {
|
|
if ((state & CLOSED) == 0) {
|
|
int s = getAndBitwiseOrState(CLOSED);
|
|
if ((s & (SOCKET_CREATED | CLOSED)) == SOCKET_CREATED) {
|
|
// close underlying socket if created
|
|
impl.close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setIPHeaderInclude(boolean on) throws IOException {
|
|
impl.setIPHeaderInclude(on);
|
|
}
|
|
|
|
|
|
|
|
public boolean getIPHeaderInclude() throws IOException {
|
|
return impl.getIPHeaderInclude();
|
|
}
|
|
|
|
public void setSendBufferSize(int size) throws SocketException {
|
|
if (size <= 0)
|
|
throw new IllegalArgumentException("negative send size");
|
|
if (isClosed())
|
|
throw new SocketException("Socket is closed");
|
|
getImpl().setOption(SocketOptions.SO_SNDBUF, size);
|
|
}
|
|
|
|
public int getSendBufferSize() throws SocketException {
|
|
if (isClosed())
|
|
throw new SocketException("Socket is closed");
|
|
int result = 0;
|
|
Object o = getImpl().getOption(SocketOptions.SO_SNDBUF);
|
|
if (o instanceof Integer i) {
|
|
result = i.intValue();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public void setReceiveBufferSize(int size) throws SocketException {
|
|
if (size <= 0)
|
|
throw new IllegalArgumentException("invalid receive size");
|
|
if (isClosed())
|
|
throw new SocketException("Socket is closed");
|
|
getImpl().setOption(SocketOptions.SO_RCVBUF, size);
|
|
}
|
|
|
|
public int getReceiveBufferSize() throws SocketException {
|
|
if (isClosed())
|
|
throw new SocketException("Socket is closed");
|
|
int result = 0;
|
|
Object o = getImpl().getOption(SocketOptions.SO_RCVBUF);
|
|
if (o instanceof Integer i) {
|
|
result = i.intValue();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
public void setUseSelectTimeout(boolean useSelect) throws IOException {
|
|
impl.setUseSelectTimeout(useSelect);
|
|
}
|
|
|
|
|
|
|
|
public boolean getUseSelectTimeout() throws IOException {
|
|
return impl.getUseSelectTimeout();
|
|
}
|
|
|
|
|
|
/* public void setSendTimeout(int timeout) throws SocketException {
|
|
impl.setSendTimeout( timeout);
|
|
}
|
|
|
|
|
|
public int getSendTimeout() throws SocketException {
|
|
return impl.getSendTimeout();
|
|
}*/
|
|
|
|
|
|
public void setSoTimeout(int timeout) throws SocketException {
|
|
if (isClosed())
|
|
throw new SocketException("Socket is closed");
|
|
if (timeout < 0)
|
|
throw new IllegalArgumentException("timeout can't be negative");
|
|
getImpl().setOption(SocketOptions.SO_TIMEOUT, timeout);
|
|
}
|
|
|
|
|
|
public int getSoTimeout() throws SocketException {
|
|
if (isClosed())
|
|
throw new SocketException("Socket is closed");
|
|
Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT);
|
|
/* extra type safety */
|
|
if (o instanceof Integer i) {
|
|
return i.intValue();
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
public void send(DatagramPacket p) throws IOException {
|
|
synchronized (p) {
|
|
if (isClosed())
|
|
throw new SocketException("Socket is closed");
|
|
InetAddress packetAddress = p.getAddress();
|
|
checkAddress(packetAddress, "send");
|
|
if (isConnected()) {
|
|
// we're connected
|
|
if (packetAddress == null) {
|
|
p.setAddress(connectedAddress);
|
|
} else if ((!packetAddress.equals(connectedAddress)) ) {
|
|
throw new IllegalArgumentException("connected address " +
|
|
"and packet address" +
|
|
" differ");
|
|
}
|
|
|
|
} else {
|
|
if (packetAddress == null) {
|
|
throw new IllegalArgumentException("Address not set");
|
|
}
|
|
}
|
|
// Check whether the socket is bound
|
|
if (!isBound())
|
|
bind(InetAddress.getByName("::0"));
|
|
// call the method to send
|
|
getImpl().send(p);
|
|
}
|
|
}
|
|
|
|
public synchronized void receive(DatagramPacket p) throws IOException {
|
|
synchronized (p) {
|
|
if (!isBound())
|
|
bind(InetAddress.getByName("::0"));
|
|
DatagramPacket tmp = null;
|
|
if (explicitFilter) {
|
|
// We have to do the filtering the old fashioned way since
|
|
// the native impl doesn't support connect or the connect
|
|
// via the impl failed, or .. "explicitFilter" may be set when
|
|
// a socket is connected via the impl, for a period of time
|
|
// when packets from other sources might be queued on socket.
|
|
boolean stop = false;
|
|
while (!stop) {
|
|
// peek at the packet to see who it is from.
|
|
DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1);
|
|
getImpl().peekData(peekPacket);
|
|
InetAddress peekAddress = peekPacket.getAddress();
|
|
if ((!connectedAddress.equals(peekAddress)) ) {
|
|
// throw the packet away and silently continue
|
|
tmp = new DatagramPacket(
|
|
new byte[1024], 1024);
|
|
getImpl().receive(tmp);
|
|
if (explicitFilter) {
|
|
if (checkFiltering(tmp)) {
|
|
stop = true;
|
|
}
|
|
}
|
|
} else {
|
|
stop = true;
|
|
}
|
|
}
|
|
}
|
|
// If the security check succeeds, or the datagram is
|
|
// connected then receive the packet
|
|
getImpl().receive(p);
|
|
if (explicitFilter && tmp == null) {
|
|
// packet was not filtered, account for it here
|
|
checkFiltering(p);
|
|
}
|
|
}
|
|
}
|
|
private boolean checkFiltering(DatagramPacket p) throws SocketException {
|
|
bytesLeftToFilter -= p.getLength();
|
|
if (bytesLeftToFilter <= 0 || getImpl().dataAvailable() <= 0) {
|
|
explicitFilter = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|