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; } }