package java.net;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.channels.DatagramChannel;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.Enumeration;
import java.util.Objects;
import java.util.Set;
import java.util.Collections;
final class NetMulticastSocket extends MulticastSocket {
private boolean bound = false;
private boolean closed = false;
private volatile boolean created;
private final Object closeLock = new Object();
private final DatagramSocketImpl impl;
private final boolean oldImpl;
private boolean explicitFilter = false;
private int bytesLeftToFilter;
static final int ST_NOT_CONNECTED = 0;
static final int ST_CONNECTED = 1;
static final int ST_CONNECTED_NO_IMPL = 2;
int connectState = ST_NOT_CONNECTED;
InetAddress connectedAddress = null;
int connectedPort = -1;
NetMulticastSocket(DatagramSocketImpl impl) {
super((MulticastSocket) null);
this.impl = Objects.requireNonNull(impl);
this.oldImpl = checkOldImpl(impl);
}
private synchronized void connectInternal(InetAddress address, int port) throws SocketException {
if (port < 0 || port > 0xFFFF) {
throw new IllegalArgumentException("connect: " + port);
}
if (address == null) {
throw new IllegalArgumentException("connect: null address");
}
checkAddress(address, "connect");
if (isClosed())
return;
SecurityManager security = System.getSecurityManager();
if (security != null) {
if (address.isMulticastAddress()) {
security.checkMulticast(address);
} else {
security.checkConnect(address.getHostAddress(), port);
security.checkAccept(address.getHostAddress(), port);
}
}
if (port == 0) {
throw new SocketException("Can't connect to port 0");
}
if (!isBound())
bind(new InetSocketAddress(0));
if (oldImpl || (impl instanceof AbstractPlainDatagramSocketImpl &&
((AbstractPlainDatagramSocketImpl) impl).nativeConnectDisabled())) {
connectState = ST_CONNECTED_NO_IMPL;
} else {
try {
getImpl().connect(address, port);
connectState = ST_CONNECTED;
int avail = getImpl().dataAvailable();
if (avail == -1) {
throw new SocketException();
}
explicitFilter = avail > 0;
if (explicitFilter) {
bytesLeftToFilter = getReceiveBufferSize();
}
} catch (SocketException se) {
connectState = ST_CONNECTED_NO_IMPL;
}
}
connectedAddress = address;
connectedPort = port;
}
private static boolean checkOldImpl(DatagramSocketImpl impl) {
try {
AccessController.doPrivileged(
new PrivilegedExceptionAction<>() {
public Void run() throws NoSuchMethodException {
Class<?>[] cl = new Class<?>[1];
cl[0] = DatagramPacket.class;
impl.getClass().getDeclaredMethod("peekData", cl);
return null;
}
});
return false;
} catch (java.security.PrivilegedActionException e) {
return true;
}
}
final DatagramSocketImpl getImpl() throws SocketException {
if (!created) {
synchronized (this) {
if (!created) {
impl.create();
created = true;
}
}
}
return impl;
}
@Override
public synchronized void bind(SocketAddress addr) throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
if (isBound())
throw new SocketException("already bound");
if (addr == null)
addr = new InetSocketAddress(0);
if (!(addr instanceof InetSocketAddress))
throw new IllegalArgumentException("Unsupported address type!");
InetSocketAddress epoint = (InetSocketAddress) addr;
if (epoint.isUnresolved())
throw new SocketException("Unresolved address");
InetAddress iaddr = epoint.getAddress();
int port = epoint.getPort();
checkAddress(iaddr, "bind");
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
sec.checkListen(port);
}
try {
getImpl().bind(port, iaddr);
} catch (SocketException e) {
getImpl().close();
throw e;
}
bound = true;
}
static void checkAddress(InetAddress addr, String op) {
if (addr == null) {
return;
}
if (!(addr instanceof Inet4Address || addr instanceof Inet6Address)) {
throw new IllegalArgumentException(op + ": invalid address type");
}
}
@Override
public void connect(InetAddress address, int port) {
try {
connectInternal(address, port);
} catch (SocketException se) {
throw new UncheckedIOException("connect failed", se);
}
}
@Override
public void connect(SocketAddress addr) throws SocketException {
if (addr == null)
throw new IllegalArgumentException("Address can't be null");
if (!(addr instanceof InetSocketAddress))
throw new IllegalArgumentException("Unsupported address type");
InetSocketAddress epoint = (InetSocketAddress) addr;
if (epoint.isUnresolved())
throw new SocketException("Unresolved address");
connectInternal(epoint.getAddress(), epoint.getPort());
}
@Override
public void disconnect() {
synchronized (this) {
if (isClosed())
return;
if (connectState == ST_CONNECTED) {
impl.disconnect();
}
connectedAddress = null;
connectedPort = -1;
connectState = ST_NOT_CONNECTED;
explicitFilter = false;
}
}
@Override
public boolean isBound() {
return bound;
}
@Override
public boolean isConnected() {
return connectState != ST_NOT_CONNECTED;
}
@Override
public InetAddress getInetAddress() {
return connectedAddress;
}
@Override
public int getPort() {
return connectedPort;
}
@Override
public SocketAddress getRemoteSocketAddress() {
if (!isConnected())
return null;
return new InetSocketAddress(getInetAddress(), getPort());
}
@Override
public SocketAddress getLocalSocketAddress() {
if (isClosed())
return null;
if (!isBound())
return null;
return new InetSocketAddress(getLocalAddress(), getLocalPort());
}
@Override
public void send(DatagramPacket p) throws IOException {
synchronized (p) {
if (isClosed())
throw new SocketException("Socket is closed");
InetAddress packetAddress = p.getAddress();
int packetPort = p.getPort();
checkAddress(packetAddress, "send");
if (connectState == ST_NOT_CONNECTED) {
if (packetAddress == null) {
throw new IllegalArgumentException("Address not set");
}
if (packetPort < 0 || packetPort > 0xFFFF)
throw new IllegalArgumentException("port out of range: " + packetPort);
SecurityManager security = System.getSecurityManager();
if (security != null) {
if (packetAddress.isMulticastAddress()) {
security.checkMulticast(packetAddress);
} else {
security.checkConnect(packetAddress.getHostAddress(),
packetPort);
}
}
if (packetPort == 0) {
throw new SocketException("Can't send to port 0");
}
} else {
if (packetAddress == null) {
p.setAddress(connectedAddress);
p.setPort(connectedPort);
} else if ((!packetAddress.equals(connectedAddress)) ||
packetPort != connectedPort) {
throw new IllegalArgumentException("connected address " +
"and packet address" +
" differ");
}
}
if (!isBound())
bind(new InetSocketAddress(0));
getImpl().send(p);
}
}
@Override
public synchronized void receive(DatagramPacket p) throws IOException {
synchronized (p) {
if (!isBound())
bind(new InetSocketAddress(0));
if (connectState == ST_NOT_CONNECTED) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
while (true) {
String peekAd = null;
int peekPort = 0;
if (!oldImpl) {
DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1);
peekPort = getImpl().peekData(peekPacket);
peekAd = peekPacket.getAddress().getHostAddress();
} else {
InetAddress adr = new InetAddress();
peekPort = getImpl().peek(adr);
peekAd = adr.getHostAddress();
}
try {
security.checkAccept(peekAd, peekPort);
break;
} catch (SecurityException se) {
DatagramPacket tmp = new DatagramPacket(new byte[1], 1);
getImpl().receive(tmp);
continue;
}
}
}
}
DatagramPacket tmp = null;
if ((connectState == ST_CONNECTED_NO_IMPL) || explicitFilter) {
boolean stop = false;
while (!stop) {
InetAddress peekAddress = null;
int peekPort = -1;
if (!oldImpl) {
DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1);
peekPort = getImpl().peekData(peekPacket);
peekAddress = peekPacket.getAddress();
} else {
peekAddress = new InetAddress();
peekPort = getImpl().peek(peekAddress);
}
if ((!connectedAddress.equals(peekAddress)) ||
(connectedPort != peekPort)) {
tmp = new DatagramPacket(
new byte[1024], 1024);
getImpl().receive(tmp);
if (explicitFilter) {
if (checkFiltering(tmp)) {
stop = true;
}
}
} else {
stop = true;
}
}
}
getImpl().receive(p);
if (explicitFilter && tmp == null) {
checkFiltering(p);
}
}
}
private boolean checkFiltering(DatagramPacket p) throws SocketException {
bytesLeftToFilter -= p.getLength();
if (bytesLeftToFilter <= 0 || getImpl().dataAvailable() <= 0) {
explicitFilter = false;
return true;
}
return false;
}
@Override
public InetAddress getLocalAddress() {
if (isClosed())
return null;
InetAddress in;
try {
in = (InetAddress) getImpl().getOption(SocketOptions.SO_BINDADDR);
if (in.isAnyLocalAddress()) {
in = InetAddress.anyLocalAddress();
}
SecurityManager s = System.getSecurityManager();
if (s != null) {
s.checkConnect(in.getHostAddress(), -1);
}
} catch (Exception e) {
in = InetAddress.anyLocalAddress();
}
return in;
}
@Override
public int getLocalPort() {
if (isClosed())
return -1;
try {
return getImpl().getLocalPort();
} catch (Exception e) {
return 0;
}
}
@Override
public synchronized void setSoTimeout(int timeout) throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
if (timeout < 0)
throw new IllegalArgumentException("timeout < 0");
getImpl().setOption(SocketOptions.SO_TIMEOUT, timeout);
}
@Override
public synchronized int getSoTimeout() throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
if (getImpl() == null)
return 0;
Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT);
if (o instanceof Integer) {
return ((Integer) o).intValue();
} else {
return 0;
}
}
@Override
public synchronized 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);
}
@Override
public synchronized 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) {
result = ((Integer) o).intValue();
}
return result;
}
@Override
public synchronized 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);
}
@Override
public synchronized 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) {
result = ((Integer) o).intValue();
}
return result;
}
@Override
public synchronized void setReuseAddress(boolean on) throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
if (oldImpl)
getImpl().setOption(SocketOptions.SO_REUSEADDR, on ? -1 : 0);
else
getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on));
}
@Override
public synchronized boolean getReuseAddress() throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
Object o = getImpl().getOption(SocketOptions.SO_REUSEADDR);
return ((Boolean) o).booleanValue();
}
@Override
public synchronized void setBroadcast(boolean on) throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
getImpl().setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(on));
}
@Override
public synchronized boolean getBroadcast() throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
return ((Boolean) (getImpl().getOption(SocketOptions.SO_BROADCAST))).booleanValue();
}
@Override
public synchronized void setTrafficClass(int tc) throws SocketException {
if (tc < 0 || tc > 255)
throw new IllegalArgumentException("tc is not in range 0 -- 255");
if (isClosed())
throw new SocketException("Socket is closed");
try {
getImpl().setOption(SocketOptions.IP_TOS, tc);
} catch (SocketException se) {
if (!isConnected())
throw se;
}
}
@Override
public synchronized int getTrafficClass() throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
return ((Integer) (getImpl().getOption(SocketOptions.IP_TOS))).intValue();
}
@Override
public void close() {
synchronized (closeLock) {
if (isClosed())
return;
impl.close();
closed = true;
}
}
@Override
public boolean isClosed() {
synchronized (closeLock) {
return closed;
}
}
@Override
public <T> DatagramSocket setOption(SocketOption<T> name, T value)
throws IOException
{
Objects.requireNonNull(name);
if (isClosed())
throw new SocketException("Socket is closed");
getImpl().setOption(name, value);
return this;
}
@Override
public <T> T getOption(SocketOption<T> name) throws IOException {
Objects.requireNonNull(name);
if (isClosed())
throw new SocketException("Socket is closed");
return getImpl().getOption(name);
}
private volatile Set<SocketOption<?>> options;
private final Object optionsLock = new Object();
@Override
public Set<SocketOption<?>> supportedOptions() {
Set<SocketOption<?>> options = this.options;
if (options != null)
return options;
synchronized (optionsLock) {
options = this.options;
if (options != null) {
return options;
}
try {
DatagramSocketImpl impl = getImpl();
options = Collections.unmodifiableSet(impl.supportedOptions());
} catch (IOException e) {
options = Collections.emptySet();
}
return this.options = options;
}
}
private boolean interfaceSet;
private final Object ttlLock = new Object();
private final Object infLock = new Object();
private InetAddress infAddress = null;
@Deprecated
@Override
public void setTTL(byte ttl) throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
getImpl().setTTL(ttl);
}
@Override
public void setTimeToLive(int ttl) throws IOException {
if (ttl < 0 || ttl > 255) {
throw new IllegalArgumentException("ttl out of range");
}
if (isClosed())
throw new SocketException("Socket is closed");
getImpl().setTimeToLive(ttl);
}
@Deprecated
@Override
public byte getTTL() throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
return getImpl().getTTL();
}
@Override
public int getTimeToLive() throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
return getImpl().getTimeToLive();
}
@Override
@Deprecated
public void joinGroup(InetAddress mcastaddr) throws IOException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
checkAddress(mcastaddr, "joinGroup");
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkMulticast(mcastaddr);
}
if (!mcastaddr.isMulticastAddress()) {
throw new SocketException("Not a multicast address");
}
NetworkInterface defaultInterface = NetworkInterface.getDefault();
if (!interfaceSet && defaultInterface != null) {
setNetworkInterface(defaultInterface);
}
getImpl().join(mcastaddr);
}
@Override
@Deprecated
public void leaveGroup(InetAddress mcastaddr) throws IOException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
checkAddress(mcastaddr, "leaveGroup");
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkMulticast(mcastaddr);
}
if (!mcastaddr.isMulticastAddress()) {
throw new SocketException("Not a multicast address");
}
getImpl().leave(mcastaddr);
}
@Override
public void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf)
throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress))
throw new IllegalArgumentException("Unsupported address type");
if (oldImpl)
throw new UnsupportedOperationException();
checkAddress(((InetSocketAddress)mcastaddr).getAddress(), "joinGroup");
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkMulticast(((InetSocketAddress)mcastaddr).getAddress());
}
if (!((InetSocketAddress)mcastaddr).getAddress().isMulticastAddress()) {
throw new SocketException("Not a multicast address");
}
getImpl().joinGroup(mcastaddr, netIf);
}
@Override
public void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf)
throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
if (mcastaddr == null || !(mcastaddr instanceof InetSocketAddress))
throw new IllegalArgumentException("Unsupported address type");
if (oldImpl)
throw new UnsupportedOperationException();
checkAddress(((InetSocketAddress)mcastaddr).getAddress(), "leaveGroup");
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkMulticast(((InetSocketAddress)mcastaddr).getAddress());
}
if (!((InetSocketAddress)mcastaddr).getAddress().isMulticastAddress()) {
throw new SocketException("Not a multicast address");
}
getImpl().leaveGroup(mcastaddr, netIf);
}
@Override
@Deprecated
public void setInterface(InetAddress inf) throws SocketException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
checkAddress(inf, "setInterface");
synchronized (infLock) {
getImpl().setOption(SocketOptions.IP_MULTICAST_IF, inf);
infAddress = inf;
interfaceSet = true;
}
}
@Override
@Deprecated
public InetAddress getInterface() throws SocketException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
synchronized (infLock) {
InetAddress ia =
(InetAddress)getImpl().getOption(SocketOptions.IP_MULTICAST_IF);
if (infAddress == null) {
return ia;
}
if (ia.equals(infAddress)) {
return ia;
}
try {
NetworkInterface ni = NetworkInterface.getByInetAddress(ia);
Enumeration<InetAddress> addrs = ni.getInetAddresses();
while (addrs.hasMoreElements()) {
InetAddress addr = addrs.nextElement();
if (addr.equals(infAddress)) {
return infAddress;
}
}
infAddress = null;
return ia;
} catch (Exception e) {
return ia;
}
}
}
@Override
public void setNetworkInterface(NetworkInterface netIf)
throws SocketException {
synchronized (infLock) {
getImpl().setOption(SocketOptions.IP_MULTICAST_IF2, netIf);
infAddress = null;
interfaceSet = true;
}
}
@Override
public NetworkInterface getNetworkInterface() throws SocketException {
NetworkInterface ni
= (NetworkInterface)getImpl().getOption(SocketOptions.IP_MULTICAST_IF2);
if (ni == null) {
InetAddress[] addrs = new InetAddress[1];
addrs[0] = InetAddress.anyLocalAddress();
return new NetworkInterface(addrs[0].getHostName(), 0, addrs);
} else {
return ni;
}
}
@Override
@Deprecated
public void setLoopbackMode(boolean disable) throws SocketException {
getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP, Boolean.valueOf(disable));
}
@Override
@Deprecated
public boolean getLoopbackMode() throws SocketException {
return ((Boolean)getImpl().getOption(SocketOptions.IP_MULTICAST_LOOP)).booleanValue();
}
@Deprecated
@Override
public void send(DatagramPacket p, byte ttl)
throws IOException {
if (isClosed())
throw new SocketException("Socket is closed");
synchronized(ttlLock) {
synchronized(p) {
InetAddress packetAddress = p.getAddress();
checkAddress(packetAddress, "send");
if (connectState == NetMulticastSocket.ST_NOT_CONNECTED) {
if (packetAddress == null) {
throw new IllegalArgumentException("Address not set");
}
SecurityManager security = System.getSecurityManager();
if (security != null) {
if (packetAddress.isMulticastAddress()) {
security.checkMulticast(packetAddress, ttl);
} else {
security.checkConnect(packetAddress.getHostAddress(),
p.getPort());
}
}
} else {
if (packetAddress == null) {
p.setAddress(connectedAddress);
p.setPort(connectedPort);
} else if ((!packetAddress.equals(connectedAddress)) ||
p.getPort() != connectedPort) {
throw new IllegalArgumentException("connected address and packet address" +
" differ");
}
}
byte dttl = getTTL();
try {
if (ttl != dttl) {
getImpl().setTTL(ttl);
}
if (p.getPort() == 0) {
throw new SocketException("Can't send to port 0");
}
getImpl().send(p);
} finally {
if (ttl != dttl) {
getImpl().setTTL(dttl);
}
}
}
}
}
}