changeset 59304:df9b06a8a184

8241072: Reimplement the Legacy DatagramSocket API Summary: Replace the underlying implementations of the java.net.DatagramSocket and java.net.MulticastSocket APIs with simpler and more modern implementations that are easy to maintain and debug. Reviewed-by: alanb, chegar, dfuchs Contributed-by: Alan Bateman <alan.bateman@oracle.com>, Chris Hegarty <chris.hegarty@oracle.com>, Daniel Fuchs <daniel.fuchs@oracle.com>, Patrick Concannon <patrick.concannon@oracle.com>
author pconcannon
date Tue, 12 May 2020 21:51:53 +0100
parents edb6ac24115b
children 231fc21356e1
files src/java.base/share/classes/java/net/DatagramSocket.java src/java.base/share/classes/java/net/DatagramSocketImpl.java src/java.base/share/classes/java/net/MulticastSocket.java src/java.base/share/classes/java/net/NetMulticastSocket.java src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java test/jdk/ProblemList.txt test/jdk/java/net/DatagramSocket/AddressNotSet.java test/jdk/java/net/DatagramSocket/B6411513.java test/jdk/java/net/DatagramSocket/DatagramTimeout.java test/jdk/java/net/DatagramSocket/InterruptibleDatagramSocket.java test/jdk/java/net/DatagramSocket/ReuseAddressTest.java test/jdk/java/net/DatagramSocket/SendCheck.java test/jdk/java/net/DatagramSocket/SendPortZero.java test/jdk/java/net/DatagramSocket/SetGetReceiveBufferSize.java test/jdk/java/net/DatagramSocket/SetGetSendBufferSize.java test/jdk/java/net/DatagramSocket/TestAfterClose.java test/jdk/java/net/DatagramSocket/UnreferencedDatagramSockets.java test/jdk/java/net/DatagramSocketImpl/TestCreate.java test/jdk/java/net/InetAddress/CheckJNI.java test/jdk/java/net/MulticastSocket/B6427403.java test/jdk/java/net/MulticastSocket/MulticastAddresses.java test/jdk/java/net/MulticastSocket/NoSetNetworkInterface.java test/jdk/java/net/MulticastSocket/Promiscuous.java test/jdk/java/net/MulticastSocket/SendPortZero.java test/jdk/java/net/MulticastSocket/SetLoopbackMode.java test/jdk/java/net/MulticastSocket/SetLoopbackModeIPv4.java test/jdk/java/net/MulticastSocket/SetLoopbackOption.java test/jdk/java/net/MulticastSocket/SetOutgoingIf.java test/jdk/java/net/MulticastSocket/SetTTLAndGetTTL.java test/jdk/java/net/MulticastSocket/SetTTLTo0.java test/jdk/java/net/MulticastSocket/UnreferencedMulticastSockets.java test/jdk/java/net/Socket/AddressTest.java test/jdk/java/net/SocketOption/AfterClose.java test/jdk/java/net/SocketOption/OptionsTest.java
diffstat 34 files changed, 1473 insertions(+), 904 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/net/DatagramSocket.java	Fri May 15 10:43:20 2020 -0400
+++ b/src/java.base/share/classes/java/net/DatagramSocket.java	Tue May 12 21:51:53 2020 +0100
@@ -29,10 +29,10 @@
 import java.io.UncheckedIOException;
 import java.nio.channels.DatagramChannel;
 import java.security.AccessController;
-import java.security.PrivilegedExceptionAction;
-import java.util.Objects;
+import java.security.PrivilegedAction;
 import java.util.Set;
-import java.util.Collections;
+import sun.net.NetProperties;
+import sun.nio.ch.DefaultSelectorProvider;
 
 /**
  * This class represents a socket for sending and receiving datagram packets.
@@ -113,118 +113,28 @@
  * @since 1.0
  */
 public class DatagramSocket implements java.io.Closeable {
-    /**
-     * Various states of this socket.
-     */
-    private boolean bound = false;
-    private boolean closed = false;
-    private volatile boolean created;
-    private final Object closeLock = new Object();
 
-    /*
-     * The implementation of this DatagramSocket.
-     */
-    private final DatagramSocketImpl impl;
+    // An instance of DatagramSocketAdaptor, NetMulticastSocket, or null
+    private final DatagramSocket delegate;
+
+    DatagramSocket delegate() {
+        if (delegate == null) {
+            throw new InternalError("Should not get here");
+        }
+        return delegate;
+    }
 
     /**
-     * Are we using an older DatagramSocketImpl?
+     * All constructors eventually call this one.
+     * @param delegate The wrapped DatagramSocket implementation, or null.
      */
-    final boolean oldImpl;
-
-    /**
-     * Set when a socket is ST_CONNECTED until we are certain
-     * that any packets which might have been received prior
-     * to calling connect() but not read by the application
-     * have been read. During this time we check the source
-     * address of all packets received to be sure they are from
-     * the connected destination. Other packets are read but
-     * silently dropped.
-     */
-    private boolean explicitFilter = false;
-    private int bytesLeftToFilter;
-    /*
-     * Connection state:
-     * ST_NOT_CONNECTED = socket not connected
-     * ST_CONNECTED = socket connected
-     * ST_CONNECTED_NO_IMPL = socket connected but not at impl level
-     */
-    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;
-
-    /*
-     * Connected address & port
-     */
-    InetAddress connectedAddress = null;
-    int connectedPort = -1;
-
-    /**
-     * Connects this socket to a remote socket address (IP address + port number).
-     * Binds socket if not already bound.
-     *
-     * @param   address The remote address.
-     * @param   port    The remote port
-     * @throws  SocketException if binding the socket fails.
-     */
-    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));
-
-        // old impls do not support connect/disconnect
-        if (oldImpl || (impl instanceof AbstractPlainDatagramSocketImpl &&
-             ((AbstractPlainDatagramSocketImpl)impl).nativeConnectDisabled())) {
-            connectState = ST_CONNECTED_NO_IMPL;
-        } else {
-            try {
-                getImpl().connect(address, port);
-
-                // socket is now connected by the impl
-                connectState = ST_CONNECTED;
-                // Do we need to filter some packets?
-                int avail = getImpl().dataAvailable();
-                if (avail == -1) {
-                    throw new SocketException();
-                }
-                explicitFilter = avail > 0;
-                if (explicitFilter) {
-                    bytesLeftToFilter = getReceiveBufferSize();
-                }
-            } catch (SocketException se) {
-
-                // connection will be emulated by DatagramSocket
-                connectState = ST_CONNECTED_NO_IMPL;
-            }
-        }
-
-        connectedAddress = address;
-        connectedPort = port;
+    DatagramSocket(DatagramSocket delegate) {
+        assert delegate == null
+                || delegate instanceof NetMulticastSocket
+                || delegate instanceof sun.nio.ch.DatagramSocketAdaptor;
+        this.delegate = delegate;
     }
 
-
     /**
      * Constructs a datagram socket and binds it to any available port
      * on the local host machine.  The socket will be bound to the
@@ -256,10 +166,7 @@
      * @since   1.4
      */
     protected DatagramSocket(DatagramSocketImpl impl) {
-        if (impl == null)
-            throw new NullPointerException();
-        this.impl = impl;
-        this.oldImpl = checkOldImpl(impl);
+        this(new NetMulticastSocket(impl));
     }
 
     /**
@@ -286,28 +193,7 @@
      * @since   1.4
      */
     public DatagramSocket(SocketAddress bindaddr) throws SocketException {
-        // Special case initialization for the DatagramChannel socket adaptor.
-        if (this instanceof sun.nio.ch.DatagramSocketAdaptor) {
-            this.impl = null;  // no DatagramSocketImpl
-            this.oldImpl = false;
-            return;
-        }
-
-        // create a datagram socket.
-        boolean multicast = (this instanceof MulticastSocket);
-        this.impl = createImpl(multicast);
-        // creates the udp socket
-        impl.create();
-        created = true;
-        this.oldImpl = checkOldImpl(impl);
-        if (bindaddr != null) {
-            try {
-                bind(bindaddr);
-            } finally {
-                if (!isBound())
-                    close();
-            }
-        }
+        this(createDelegate(bindaddr, DatagramSocket.class));
     }
 
     /**
@@ -363,67 +249,6 @@
     }
 
     /**
-     * Return true if the given DatagramSocketImpl is an "old" impl. An old impl
-     * is one that doesn't implement the abstract methods added in Java SE 1.4.
-     */
-    private static boolean checkOldImpl(DatagramSocketImpl impl) {
-        // DatagramSocketImpl.peekData() is a protected method, therefore we need to use
-        // getDeclaredMethod, therefore we need permission to access the member
-        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;
-        }
-    }
-
-    static Class<?> implClass = null;
-
-    /**
-     * Creates a DatagramSocketImpl.
-     * @param multicast true if the DatagramSocketImpl is for a MulticastSocket
-     */
-    private static DatagramSocketImpl createImpl(boolean multicast) throws SocketException {
-        DatagramSocketImpl impl;
-        DatagramSocketImplFactory factory = DatagramSocket.factory;
-        if (factory != null) {
-            impl = factory.createDatagramSocketImpl();
-        } else {
-            impl = DefaultDatagramSocketImplFactory.createDatagramSocketImpl(multicast);
-        }
-        return impl;
-    }
-
-    /**
-     * Return the {@code DatagramSocketImpl} attached to this socket,
-     * creating the socket if not already created.
-     *
-     * @return  the {@code DatagramSocketImpl} attached to that
-     *          DatagramSocket
-     * @throws SocketException if creating the socket fails
-     * @since 1.4
-     */
-    final DatagramSocketImpl getImpl() throws SocketException {
-        if (!created) {
-            synchronized (this) {
-                if (!created)  {
-                    impl.create();
-                    created = true;
-                }
-            }
-        }
-        return impl;
-    }
-
-    /**
      * Binds this DatagramSocket to a specific address and port.
      * <p>
      * If the address is {@code null}, then the system will pick up
@@ -438,41 +263,8 @@
      *         not supported by this socket.
      * @since 1.4
      */
-    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;
-    }
-
-    void checkAddress (InetAddress addr, String op) {
-        if (addr == null) {
-            return;
-        }
-        if (!(addr instanceof Inet4Address || addr instanceof Inet6Address)) {
-            throw new IllegalArgumentException(op + ": invalid address type");
-        }
+    public void bind(SocketAddress addr) throws SocketException {
+        delegate().bind(addr);
     }
 
     /**
@@ -534,11 +326,7 @@
      * @since 1.2
      */
     public void connect(InetAddress address, int port) {
-        try {
-            connectInternal(address, port);
-        } catch (SocketException se) {
-            throw new UncheckedIOException("connect failed", se);
-        }
+        delegate().connect(address, port);
     }
 
     /**
@@ -566,14 +354,7 @@
      * @since 1.4
      */
     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());
+        delegate().connect(addr);
     }
 
     /**
@@ -594,17 +375,7 @@
      * @since 1.2
      */
     public void disconnect() {
-        synchronized (this) {
-            if (isClosed())
-                return;
-            if (connectState == ST_CONNECTED) {
-                impl.disconnect ();
-            }
-            connectedAddress = null;
-            connectedPort = -1;
-            connectState = ST_NOT_CONNECTED;
-            explicitFilter = false;
-        }
+        delegate().disconnect();
     }
 
     /**
@@ -618,7 +389,7 @@
      * @since 1.4
      */
     public boolean isBound() {
-        return bound;
+        return delegate().isBound();
     }
 
     /**
@@ -632,7 +403,7 @@
      * @since 1.4
      */
     public boolean isConnected() {
-        return connectState != ST_NOT_CONNECTED;
+        return delegate().isConnected();
     }
 
     /**
@@ -647,7 +418,7 @@
      * @since 1.2
      */
     public InetAddress getInetAddress() {
-        return connectedAddress;
+        return delegate().getInetAddress();
     }
 
     /**
@@ -662,7 +433,7 @@
      * @since 1.2
      */
     public int getPort() {
-        return connectedPort;
+        return delegate().getPort();
     }
 
     /**
@@ -682,9 +453,7 @@
      * @since 1.4
      */
     public SocketAddress getRemoteSocketAddress() {
-        if (!isConnected())
-            return null;
-        return new InetSocketAddress(getInetAddress(), getPort());
+        return delegate().getRemoteSocketAddress();
     }
 
     /**
@@ -698,11 +467,7 @@
      * @since 1.4
      */
     public SocketAddress getLocalSocketAddress() {
-        if (isClosed())
-            return null;
-        if (!isBound())
-            return null;
-        return new InetSocketAddress(getLocalAddress(), getLocalPort());
+        return delegate().getLocalSocketAddress();
     }
 
     /**
@@ -748,54 +513,7 @@
      * @spec JSR-51
      */
     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);
-                // check the address is ok with the security manager on every send.
-                SecurityManager security = System.getSecurityManager();
-
-                // The reason you want to synchronize on datagram packet
-                // is because you don't want an applet to change the address
-                // while you are trying to send the packet for example
-                // after the security check but before the send.
-                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 {
-                // we're connected
-                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");
-                }
-            }
-            // Check whether the socket is bound
-            if (!isBound())
-                bind(new InetSocketAddress(0));
-            // call the  method to send
-            getImpl().send(p);
-        }
+        delegate().send(p);
     }
 
     /**
@@ -831,105 +549,8 @@
      * @revised 1.4
      * @spec JSR-51
      */
-    public synchronized void receive(DatagramPacket p) throws IOException {
-        synchronized (p) {
-            if (!isBound())
-                bind(new InetSocketAddress(0));
-            if (connectState == ST_NOT_CONNECTED) {
-                // check the address is ok with the security manager before every recv.
-                SecurityManager security = System.getSecurityManager();
-                if (security != null) {
-                    while(true) {
-                        String peekAd = null;
-                        int peekPort = 0;
-                        // peek at the packet to see who it is from.
-                        if (!oldImpl) {
-                            // We can use the new peekData() API
-                            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);
-                            // security check succeeded - so now break
-                            // and recv the packet.
-                            break;
-                        } catch (SecurityException se) {
-                            // Throw away the offending packet by consuming
-                            // it in a tmp buffer.
-                            DatagramPacket tmp = new DatagramPacket(new byte[1], 1);
-                            getImpl().receive(tmp);
-
-                            // silently discard the offending packet
-                            // and continue: unknown/malicious
-                            // entities on nets should not make
-                            // runtime throw security exception and
-                            // disrupt the applet by sending random
-                            // datagram packets.
-                            continue;
-                        }
-                    } // end of while
-                }
-            }
-            DatagramPacket tmp = null;
-            if ((connectState == ST_CONNECTED_NO_IMPL) || 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) {
-                    InetAddress peekAddress = null;
-                    int peekPort = -1;
-                    // peek at the packet to see who it is from.
-                    if (!oldImpl) {
-                        // We can use the new peekData() API
-                        DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1);
-                        peekPort = getImpl().peekData(peekPacket);
-                        peekAddress = peekPacket.getAddress();
-                    } else {
-                        // this api only works for IPv4
-                        peekAddress = new InetAddress();
-                        peekPort = getImpl().peek(peekAddress);
-                    }
-                    if ((!connectedAddress.equals(peekAddress)) ||
-                        (connectedPort != peekPort)) {
-                        // 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;
+    public void receive(DatagramPacket p) throws IOException {
+        delegate().receive(p);
     }
 
     /**
@@ -951,22 +572,7 @@
      * @since   1.1
      */
     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(); // "0.0.0.0"
-        }
-        return in;
+        return delegate().getLocalAddress();
     }
 
     /**
@@ -978,13 +584,7 @@
      *          {@code 0} if it is not bound yet.
      */
     public int getLocalPort() {
-        if (isClosed())
-            return -1;
-        try {
-            return getImpl().getLocalPort();
-        } catch (Exception e) {
-            return 0;
-        }
+        return delegate().getLocalPort();
     }
 
     /**
@@ -1004,12 +604,8 @@
      * @since   1.1
      * @see #getSoTimeout()
      */
-    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);
+    public void setSoTimeout(int timeout) throws SocketException {
+        delegate().setSoTimeout(timeout);
     }
 
     /**
@@ -1021,18 +617,8 @@
      * @since   1.1
      * @see #setSoTimeout(int)
      */
-    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);
-        /* extra type safety */
-        if (o instanceof Integer) {
-            return ((Integer) o).intValue();
-        } else {
-            return 0;
-        }
+    public int getSoTimeout() throws SocketException {
+        return delegate().getSoTimeout();
     }
 
     /**
@@ -1065,13 +651,8 @@
      * @see #getSendBufferSize()
      * @since 1.2
      */
-    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);
+    public void setSendBufferSize(int size) throws SocketException {
+        delegate().setSendBufferSize(size);
     }
 
     /**
@@ -1084,15 +665,8 @@
      * @see #setSendBufferSize
      * @since 1.2
      */
-    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;
+    public int getSendBufferSize() throws SocketException {
+        return delegate().getSendBufferSize();
     }
 
     /**
@@ -1124,13 +698,8 @@
      * @see #getReceiveBufferSize()
      * @since 1.2
      */
-    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);
+    public void setReceiveBufferSize(int size) throws SocketException {
+        delegate().setReceiveBufferSize(size);
     }
 
     /**
@@ -1142,15 +711,8 @@
      * @see #setReceiveBufferSize(int)
      * @since 1.2
      */
-    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;
+    public int getReceiveBufferSize() throws SocketException {
+        return delegate().getReceiveBufferSize();
     }
 
     /**
@@ -1187,14 +749,8 @@
      * @see #isBound()
      * @see #isClosed()
      */
-    public synchronized void setReuseAddress(boolean on) throws SocketException {
-        if (isClosed())
-            throw new SocketException("Socket is closed");
-        // Integer instead of Boolean for compatibility with older DatagramSocketImpl
-        if (oldImpl)
-            getImpl().setOption(SocketOptions.SO_REUSEADDR, on?-1:0);
-        else
-            getImpl().setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(on));
+    public void setReuseAddress(boolean on) throws SocketException {
+        delegate().setReuseAddress(on);
     }
 
     /**
@@ -1206,11 +762,8 @@
      * @since   1.4
      * @see #setReuseAddress(boolean)
      */
-    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();
+    public boolean getReuseAddress() throws SocketException {
+        return delegate().getReuseAddress();
     }
 
     /**
@@ -1230,10 +783,8 @@
      * @since 1.4
      * @see #getBroadcast()
      */
-    public synchronized void setBroadcast(boolean on) throws SocketException {
-        if (isClosed())
-            throw new SocketException("Socket is closed");
-        getImpl().setOption(SocketOptions.SO_BROADCAST, Boolean.valueOf(on));
+    public void setBroadcast(boolean on) throws SocketException {
+        delegate().setBroadcast(on);
     }
 
     /**
@@ -1244,10 +795,8 @@
      * @since 1.4
      * @see #setBroadcast(boolean)
      */
-    public synchronized boolean getBroadcast() throws SocketException {
-        if (isClosed())
-            throw new SocketException("Socket is closed");
-        return ((Boolean)(getImpl().getOption(SocketOptions.SO_BROADCAST))).booleanValue();
+    public boolean getBroadcast() throws SocketException {
+        return delegate().getBroadcast();
     }
 
     /**
@@ -1287,20 +836,8 @@
      * @since 1.4
      * @see #getTrafficClass
      */
-    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) {
-            // not supported if socket already connected
-            // Solaris returns error in such cases
-            if(!isConnected())
-                throw se;
-        }
+    public void setTrafficClass(int tc) throws SocketException {
+        delegate().setTrafficClass(tc);
     }
 
     /**
@@ -1319,10 +856,8 @@
      * @since 1.4
      * @see #setTrafficClass(int)
      */
-    public synchronized int getTrafficClass() throws SocketException {
-        if (isClosed())
-            throw new SocketException("Socket is closed");
-        return ((Integer)(getImpl().getOption(SocketOptions.IP_TOS))).intValue();
+    public int getTrafficClass() throws SocketException {
+        return delegate().getTrafficClass();
     }
 
     /**
@@ -1338,12 +873,7 @@
      * @spec JSR-51
      */
     public void close() {
-        synchronized(closeLock) {
-            if (isClosed())
-                return;
-            impl.close();
-            closed = true;
-        }
+        delegate().close();
     }
 
     /**
@@ -1353,9 +883,7 @@
      * @since 1.4
      */
     public boolean isClosed() {
-        synchronized(closeLock) {
-            return closed;
-        }
+        return delegate().isClosed();
     }
 
     /**
@@ -1409,7 +937,7 @@
      */
     public static synchronized void
     setDatagramSocketImplFactory(DatagramSocketImplFactory fac)
-       throws IOException
+            throws IOException
     {
         if (factory != null) {
             throw new SocketException("factory already defined");
@@ -1452,10 +980,7 @@
     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);
+        delegate().setOption(name, value);
         return this;
     }
 
@@ -1483,15 +1008,9 @@
      * @since 9
      */
     public <T> T getOption(SocketOption<T> name) throws IOException {
-        Objects.requireNonNull(name);
-        if (isClosed())
-            throw new SocketException("Socket is closed");
-        return getImpl().getOption(name);
+        return delegate().getOption(name);
     }
 
-    private volatile Set<SocketOption<?>> options;
-    private final Object optionsLock = new Object();
-
     /**
      * Returns a set of the socket options supported by this socket.
      *
@@ -1504,22 +1023,117 @@
      * @since 9
      */
     public Set<SocketOption<?>> supportedOptions() {
-        Set<SocketOption<?>> options = this.options;
-        if (options != null)
-            return options;
+        return delegate().supportedOptions();
+    }
 
-        synchronized (optionsLock) {
-            options = this.options;
-            if (options != null)
-                return options;
+    // Temporary solution until JDK-8237352 is addressed
+    private static final SocketAddress NO_DELEGATE = new SocketAddress() {};
+    private static final boolean USE_PLAINDATAGRAMSOCKET = usePlainDatagramSocketImpl();
 
+    private static boolean usePlainDatagramSocketImpl() {
+        PrivilegedAction<String> pa = () -> NetProperties.get("jdk.net.usePlainDatagramSocketImpl");
+        String s = AccessController.doPrivileged(pa);
+        return (s != null) && (s.isEmpty() || s.equalsIgnoreCase("true"));
+    }
+
+    /**
+     * Best effort to convert an {@link IOException}
+     * into a {@link SocketException}.
+     *
+     * @param e an instance of {@link IOException}
+     * @return an instance of {@link SocketException}
+     */
+    private static SocketException toSocketException(IOException e) {
+        if (e instanceof SocketException)
+            return (SocketException) e;
+        Throwable cause = e.getCause();
+        if (cause instanceof SocketException)
+            return (SocketException) cause;
+        SocketException se = new SocketException(e.getMessage());
+        se.initCause(e);
+        return se;
+    }
+
+    /**
+     * Creates a delegate for the specific requested {@code type}. This method should
+     * only be called by {@code DatagramSocket} and {@code MulticastSocket}
+     * public constructors.
+     *
+     * @param bindaddr An address to bind to, or {@code null} if creating an unbound
+     *                 socket.
+     * @param type     This is either {@code MulticastSocket.class}, if the delegate needs
+     *                 to support joining multicast groups, or {@code DatagramSocket.class},
+     *                 if it doesn't. Typically, this will be {@code DatagramSocket.class}
+     *                 when creating a delegate for {@code DatagramSocket}, and
+     *                 {@code MulticastSocket.class} when creating a delegate for
+     *                 {@code MulticastSocket}.
+     * @param <T>      The target type for which the delegate is created.
+     *                 This is either {@code java.net.DatagramSocket} or
+     *                 {@code java.net.MulticastSocket}.
+     * @return {@code null} if {@code bindaddr == NO_DELEGATE}, otherwise returns a
+     * delegate for the requested {@code type}.
+     * @throws SocketException if an exception occurs while creating or binding the
+     *                         the delegate.
+     */
+    static <T extends DatagramSocket> T createDelegate(SocketAddress bindaddr, Class<T> type)
+            throws SocketException {
+
+        // Temporary solution until JDK-8237352 is addressed
+        if (bindaddr == NO_DELEGATE) return null;
+
+        assert type == DatagramSocket.class || type == MulticastSocket.class;
+        boolean multicast = (type == MulticastSocket.class);
+        DatagramSocket delegate = null;
+        boolean initialized = false;
+        try {
+            DatagramSocketImplFactory factory = DatagramSocket.factory;
+            if (USE_PLAINDATAGRAMSOCKET || factory != null) {
+                // create legacy DatagramSocket delegate
+                DatagramSocketImpl impl;
+                if (factory != null) {
+                    impl = factory.createDatagramSocketImpl();
+                } else {
+                    impl = DefaultDatagramSocketImplFactory.createDatagramSocketImpl(multicast);
+                }
+                delegate = new NetMulticastSocket(impl);
+                ((NetMulticastSocket) delegate).getImpl(); // ensure impl.create() is called.
+            } else {
+                // create NIO adaptor
+                delegate = DefaultSelectorProvider.get()
+                        .openUninterruptibleDatagramChannel()
+                        .socket();
+            }
+
+            if (multicast) {
+                // set reuseaddress if multicasting
+                // (must be set before binding)
+                delegate.setReuseAddress(true);
+            }
+
+            if (bindaddr != null) {
+                // bind if needed
+                delegate.bind(bindaddr);
+            }
+
+            // enable broadcast if possible
             try {
-                DatagramSocketImpl impl = getImpl();
-                options = Collections.unmodifiableSet(impl.supportedOptions());
-            } catch (IOException e) {
-                options = Collections.emptySet();
+                delegate.setBroadcast(true);
+            } catch (IOException ioe) {
             }
-            return this.options = options;
+
+            initialized = true;
+        } catch (IOException ioe) {
+            throw toSocketException(ioe);
+        } finally {
+            // make sure the delegate is closed if anything
+            // went wrong
+            if (!initialized && delegate != null) {
+                delegate.close();
+            }
         }
+        @SuppressWarnings("unchecked")
+        T result = (T) delegate;
+        return result;
     }
+
 }
--- a/src/java.base/share/classes/java/net/DatagramSocketImpl.java	Fri May 15 10:43:20 2020 -0400
+++ b/src/java.base/share/classes/java/net/DatagramSocketImpl.java	Tue May 12 21:51:53 2020 +0100
@@ -32,6 +32,25 @@
 
 /**
  * Abstract datagram and multicast socket implementation base class.
+ *
+ * @implNote Sockets created with the {@code DatagramSocket} and {@code
+ * MulticastSocket} public constructors historically delegated all socket
+ * operations to a {@code DatagramSocketImpl} implementation named
+ * "PlainDatagramSocketImpl". {@code DatagramSocket} and {@code MulticastSocket}
+ * have since been changed to a new implementation based on {@code DatagramChannel}.
+ * The JDK continues to ship with the older implementation to allow code to run
+ * that depends on unspecified behavior that differs between the old and new
+ * implementations. The old implementation will be used if the Java virtual
+ * machine is started with the system property {@systemProperty
+ * jdk.net.usePlainDatagramSocketImpl} set to use the old implementation. It may
+ * also be set in the JDK's network configuration file, located in {@code
+ * ${java.home}/conf/net.properties}. The value of the property is the string
+ * representation of a boolean. If set without a value then it defaults to {@code
+ * true}, hence running with {@code -Djdk.net.usePlainDatagramSocketImpl} or
+ * {@code -Djdk.net.usePlainDatagramSocketImpl=true} will configure the Java
+ * virtual machine to use the old implementation. The property and old
+ * implementation will be removed in a future version.
+ *
  * @author Pavani Diwanji
  * @since  1.1
  */
--- a/src/java.base/share/classes/java/net/MulticastSocket.java	Fri May 15 10:43:20 2020 -0400
+++ b/src/java.base/share/classes/java/net/MulticastSocket.java	Tue May 12 21:51:53 2020 +0100
@@ -28,9 +28,6 @@
 import java.io.IOException;
 import java.nio.channels.DatagramChannel;
 import java.nio.channels.MulticastChannel;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.Set;
 
 /**
  * The multicast datagram socket class is useful for sending
@@ -135,11 +132,19 @@
  */
 public class MulticastSocket extends DatagramSocket {
 
+    @Override
+    final MulticastSocket delegate() {
+        return (MulticastSocket) super.delegate();
+    }
+
     /**
-     * Used on some platforms to record if an outgoing interface
-     * has been set for this socket.
+     * Create a MulticastSocket that delegates to the given delegate if not null.
+     * @param delegate the delegate, can be null.
      */
-    private boolean interfaceSet;
+    MulticastSocket(MulticastSocket delegate)  {
+        super(delegate);
+    }
+
 
     /**
      * Create a multicast socket.
@@ -216,45 +221,10 @@
      * @since 1.4
      */
     public MulticastSocket(SocketAddress bindaddr) throws IOException {
-        super((SocketAddress) null);
-
-        // No further initialization when this is a DatagramChannel socket adaptor
-        if (this instanceof sun.nio.ch.DatagramSocketAdaptor)
-            return;
-
-        // Enable SO_REUSEADDR before binding
-        setReuseAddress(true);
-
-        if (bindaddr != null) {
-            try {
-                bind(bindaddr);
-            } finally {
-                if (!isBound()) {
-                    close();
-                }
-            }
-        }
+        this(createDelegate(bindaddr, MulticastSocket.class));
     }
 
     /**
-     * The lock on the socket's TTL. This is for set/getTTL and
-     * send(packet,ttl).
-     */
-    private Object ttlLock = new Object();
-
-    /**
-     * The lock on the socket's interface - used by setInterface
-     * and getInterface
-     */
-    private Object infLock = new Object();
-
-    /**
-     * The "last" interface set by setInterface on this MulticastSocket
-     */
-    private InetAddress infAddress = null;
-
-
-    /**
      * Set the default time-to-live for multicast packets sent out
      * on this {@code MulticastSocket} in order to control the
      * scope of the multicasts.
@@ -271,9 +241,7 @@
      */
     @Deprecated
     public void setTTL(byte ttl) throws IOException {
-        if (isClosed())
-            throw new SocketException("Socket is closed");
-        getImpl().setTTL(ttl);
+        delegate().setTTL(ttl);
     }
 
     /**
@@ -297,12 +265,7 @@
      * @since 1.2
      */
     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);
+        delegate().setTimeToLive(ttl);
     }
 
     /**
@@ -318,9 +281,7 @@
      */
     @Deprecated
     public byte getTTL() throws IOException {
-        if (isClosed())
-            throw new SocketException("Socket is closed");
-        return getImpl().getTTL();
+        return delegate().getTTL();
     }
 
     /**
@@ -333,9 +294,7 @@
      * @since 1.2
      */
     public int getTimeToLive() throws IOException {
-        if (isClosed())
-            throw new SocketException("Socket is closed");
-        return getImpl().getTimeToLive();
+        return delegate().getTimeToLive();
     }
 
     /**
@@ -359,31 +318,7 @@
      */
     @Deprecated(since="14")
     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");
-        }
-
-        /**
-         * required for some platforms where it's not possible to join
-         * a group without setting the interface first.
-         */
-        NetworkInterface defaultInterface = NetworkInterface.getDefault();
-
-        if (!interfaceSet && defaultInterface != null) {
-            setNetworkInterface(defaultInterface);
-        }
-
-        getImpl().join(mcastaddr);
+        delegate().joinGroup(mcastaddr);
     }
 
     /**
@@ -406,21 +341,7 @@
      */
     @Deprecated(since="14")
     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);
+        delegate().leaveGroup(mcastaddr);
     }
 
     /**
@@ -452,26 +373,7 @@
      */
     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);
+        delegate().joinGroup(mcastaddr, netIf);
     }
 
     /**
@@ -500,26 +402,7 @@
      */
     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);
+        delegate().leaveGroup(mcastaddr, netIf);
      }
 
     /**
@@ -537,15 +420,7 @@
      */
     @Deprecated(since="14")
     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;
-        }
+        delegate().setInterface(inf);
     }
 
     /**
@@ -565,53 +440,7 @@
      */
     @Deprecated(since="14")
     public InetAddress getInterface() throws SocketException {
-        if (isClosed()) {
-            throw new SocketException("Socket is closed");
-        }
-        synchronized (infLock) {
-            InetAddress ia =
-                (InetAddress)getImpl().getOption(SocketOptions.IP_MULTICAST_IF);
-
-            /**
-             * No previous setInterface or interface can be
-             * set using setNetworkInterface
-             */
-            if (infAddress == null) {
-                return ia;
-            }
-
-            /**
-             * Same interface set with setInterface?
-             */
-            if (ia.equals(infAddress)) {
-                return ia;
-            }
-
-            /**
-             * Different InetAddress from what we set with setInterface
-             * so enumerate the current interface to see if the
-             * address set by setInterface is bound to this interface.
-             */
-            try {
-                NetworkInterface ni = NetworkInterface.getByInetAddress(ia);
-                Enumeration<InetAddress> addrs = ni.getInetAddresses();
-                while (addrs.hasMoreElements()) {
-                    InetAddress addr = addrs.nextElement();
-                    if (addr.equals(infAddress)) {
-                        return infAddress;
-                    }
-                }
-
-                /**
-                 * No match so reset infAddress to indicate that the
-                 * interface has changed via means
-                 */
-                infAddress = null;
-                return ia;
-            } catch (Exception e) {
-                return ia;
-            }
-        }
+        return delegate().getInterface();
     }
 
     /**
@@ -626,12 +455,7 @@
      */
     public void setNetworkInterface(NetworkInterface netIf)
         throws SocketException {
-
-        synchronized (infLock) {
-            getImpl().setOption(SocketOptions.IP_MULTICAST_IF2, netIf);
-            infAddress = null;
-            interfaceSet = true;
-        }
+        delegate().setNetworkInterface(netIf);
     }
 
     /**
@@ -646,15 +470,7 @@
      * @since  1.4
      */
     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;
-        }
+        return delegate().getNetworkInterface();
     }
 
     /**
@@ -678,7 +494,7 @@
      */
     @Deprecated(since="14")
     public void setLoopbackMode(boolean disable) throws SocketException {
-        getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP, Boolean.valueOf(disable));
+        delegate().setLoopbackMode(disable);
     }
 
     /**
@@ -694,7 +510,7 @@
      */
     @Deprecated(since="14")
     public boolean getLoopbackMode() throws SocketException {
-        return ((Boolean)getImpl().getOption(SocketOptions.IP_MULTICAST_LOOP)).booleanValue();
+        return delegate().getLoopbackMode();
     }
 
     /**
@@ -755,60 +571,6 @@
     @Deprecated
     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();
-                    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);
-                        // Security manager makes sure that the multicast address
-                        // is allowed one and that the ttl used is less
-                        // than the allowed maxttl.
-                        SecurityManager security = System.getSecurityManager();
-                        if (security != null) {
-                            if (packetAddress.isMulticastAddress()) {
-                                security.checkMulticast(packetAddress, ttl);
-                            } else {
-                                security.checkConnect(packetAddress.getHostAddress(),
-                                        packetPort);
-                            }
-                        }
-                    } else {
-                        // we're connected
-                        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");
-                        }
-                    }
-                    byte dttl = getTTL();
-                    try {
-                        if (ttl != dttl) {
-                            // set the ttl
-                            getImpl().setTTL(ttl);
-                        }
-                        if (packetPort == 0) {
-                            throw new SocketException("Can't send to port 0");
-                        }
-                        // call the datagram method to send
-                        getImpl().send(p);
-                    } finally {
-                        // set it back to default
-                        if (ttl != dttl) {
-                            getImpl().setTTL(dttl);
-                        }
-                    }
-                } // synch p
-            }  //synch ttl
-    } //method
+        delegate().send(p, ttl);
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/net/NetMulticastSocket.java	Tue May 12 21:51:53 2020 +0100
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+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;
+
+/**
+ * A multicast datagram socket that delegates socket operations to a
+ * {@link DatagramSocketImpl}.
+ *
+ * This class overrides every public method defined by {@link DatagramSocket}
+ * and {@link MulticastSocket}.
+ */
+final class NetMulticastSocket extends MulticastSocket {
+    /**
+     * Various states of this socket.
+     */
+    private boolean bound = false;
+    private boolean closed = false;
+    private volatile boolean created;
+    private final Object closeLock = new Object();
+
+    /*
+     * The implementation of this DatagramSocket.
+     */
+    private final DatagramSocketImpl impl;
+
+    /**
+     * Are we using an older DatagramSocketImpl?
+     */
+    private final boolean oldImpl;
+
+    /**
+     * Set when a socket is ST_CONNECTED until we are certain
+     * that any packets which might have been received prior
+     * to calling connect() but not read by the application
+     * have been read. During this time we check the source
+     * address of all packets received to be sure they are from
+     * the connected destination. Other packets are read but
+     * silently dropped.
+     */
+    private boolean explicitFilter = false;
+    private int bytesLeftToFilter;
+    /*
+     * Connection state:
+     * ST_NOT_CONNECTED = socket not connected
+     * ST_CONNECTED = socket connected
+     * ST_CONNECTED_NO_IMPL = socket connected but not at impl level
+     */
+    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;
+
+    /*
+     * Connected address & port
+     */
+    InetAddress connectedAddress = null;
+    int connectedPort = -1;
+
+    /**
+     * This constructor is also used by {@link DatagramSocket#DatagramSocket(DatagramSocketImpl)}.
+     * @param impl The impl used in this instance.
+     */
+    NetMulticastSocket(DatagramSocketImpl impl) {
+        super((MulticastSocket) null);
+        this.impl = Objects.requireNonNull(impl);
+        this.oldImpl = checkOldImpl(impl);
+    }
+
+    /**
+     * Connects this socket to a remote socket address (IP address + port number).
+     * Binds socket if not already bound.
+     *
+     * @param   address The remote address.
+     * @param   port    The remote port
+     * @throws  SocketException if binding the socket fails.
+     */
+    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));
+
+        // old impls do not support connect/disconnect
+        if (oldImpl || (impl instanceof AbstractPlainDatagramSocketImpl &&
+                ((AbstractPlainDatagramSocketImpl) impl).nativeConnectDisabled())) {
+            connectState = ST_CONNECTED_NO_IMPL;
+        } else {
+            try {
+                getImpl().connect(address, port);
+
+                // socket is now connected by the impl
+                connectState = ST_CONNECTED;
+                // Do we need to filter some packets?
+                int avail = getImpl().dataAvailable();
+                if (avail == -1) {
+                    throw new SocketException();
+                }
+                explicitFilter = avail > 0;
+                if (explicitFilter) {
+                    bytesLeftToFilter = getReceiveBufferSize();
+                }
+            } catch (SocketException se) {
+
+                // connection will be emulated by DatagramSocket
+                connectState = ST_CONNECTED_NO_IMPL;
+            }
+        }
+
+        connectedAddress = address;
+        connectedPort = port;
+    }
+
+    /**
+     * Return true if the given DatagramSocketImpl is an "old" impl. An old impl
+     * is one that doesn't implement the abstract methods added in Java SE 1.4.
+     */
+    private static boolean checkOldImpl(DatagramSocketImpl impl) {
+        // DatagramSocketImpl.peekData() is a protected method, therefore we need to use
+        // getDeclaredMethod, therefore we need permission to access the member
+        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;
+        }
+    }
+
+    /**
+     * Return the {@code DatagramSocketImpl} attached to this socket,
+     * creating the socket if not already created.
+     *
+     * @return  the {@code DatagramSocketImpl} attached to that
+     *          DatagramSocket
+     * @throws SocketException if creating the socket fails
+     * @since 1.4
+     */
+    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);
+                // check the address is ok with the security manager on every send.
+                SecurityManager security = System.getSecurityManager();
+
+                // The reason you want to synchronize on datagram packet
+                // is because you don't want an applet to change the address
+                // while you are trying to send the packet for example
+                // after the security check but before the send.
+                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 {
+                // we're connected
+                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");
+                }
+            }
+            // Check whether the socket is bound
+            if (!isBound())
+                bind(new InetSocketAddress(0));
+            // call the  method to send
+            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) {
+                // check the address is ok with the security manager before every recv.
+                SecurityManager security = System.getSecurityManager();
+                if (security != null) {
+                    while (true) {
+                        String peekAd = null;
+                        int peekPort = 0;
+                        // peek at the packet to see who it is from.
+                        if (!oldImpl) {
+                            // We can use the new peekData() API
+                            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);
+                            // security check succeeded - so now break
+                            // and recv the packet.
+                            break;
+                        } catch (SecurityException se) {
+                            // Throw away the offending packet by consuming
+                            // it in a tmp buffer.
+                            DatagramPacket tmp = new DatagramPacket(new byte[1], 1);
+                            getImpl().receive(tmp);
+
+                            // silently discard the offending packet
+                            // and continue: unknown/malicious
+                            // entities on nets should not make
+                            // runtime throw security exception and
+                            // disrupt the applet by sending random
+                            // datagram packets.
+                            continue;
+                        }
+                    } // end of while
+                }
+            }
+            DatagramPacket tmp = null;
+            if ((connectState == ST_CONNECTED_NO_IMPL) || 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) {
+                    InetAddress peekAddress = null;
+                    int peekPort = -1;
+                    // peek at the packet to see who it is from.
+                    if (!oldImpl) {
+                        // We can use the new peekData() API
+                        DatagramPacket peekPacket = new DatagramPacket(new byte[1], 1);
+                        peekPort = getImpl().peekData(peekPacket);
+                        peekAddress = peekPacket.getAddress();
+                    } else {
+                        // this api only works for IPv4
+                        peekAddress = new InetAddress();
+                        peekPort = getImpl().peek(peekAddress);
+                    }
+                    if ((!connectedAddress.equals(peekAddress)) ||
+                            (connectedPort != peekPort)) {
+                        // 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;
+    }
+
+    @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(); // "0.0.0.0"
+        }
+        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);
+        /* extra type safety */
+        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");
+        // Integer instead of Boolean for compatibility with older DatagramSocketImpl
+        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) {
+            // not supported if socket already connected
+            // Solaris returns error in such cases
+            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;
+        }
+    }
+
+    // Multicast socket support
+
+    /**
+     * Used on some platforms to record if an outgoing interface
+     * has been set for this socket.
+     */
+    private boolean interfaceSet;
+
+    /**
+     * The lock on the socket's TTL. This is for set/getTTL and
+     * send(packet,ttl).
+     */
+    private final Object ttlLock = new Object();
+
+    /**
+     * The lock on the socket's interface - used by setInterface
+     * and getInterface
+     */
+    private final Object infLock = new Object();
+
+    /**
+     * The "last" interface set by setInterface on this MulticastSocket
+     */
+    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");
+        }
+
+        /**
+         * required for some platforms where it's not possible to join
+         * a group without setting the interface first.
+         */
+        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);
+
+            /**
+             * No previous setInterface or interface can be
+             * set using setNetworkInterface
+             */
+            if (infAddress == null) {
+                return ia;
+            }
+
+            /**
+             * Same interface set with setInterface?
+             */
+            if (ia.equals(infAddress)) {
+                return ia;
+            }
+
+            /**
+             * Different InetAddress from what we set with setInterface
+             * so enumerate the current interface to see if the
+             * address set by setInterface is bound to this interface.
+             */
+            try {
+                NetworkInterface ni = NetworkInterface.getByInetAddress(ia);
+                Enumeration<InetAddress> addrs = ni.getInetAddresses();
+                while (addrs.hasMoreElements()) {
+                    InetAddress addr = addrs.nextElement();
+                    if (addr.equals(infAddress)) {
+                        return infAddress;
+                    }
+                }
+
+                /**
+                 * No match so reset infAddress to indicate that the
+                 * interface has changed via means
+                 */
+                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");
+                    }
+                    // Security manager makes sure that the multicast address
+                    // is allowed one and that the ttl used is less
+                    // than the allowed maxttl.
+                    SecurityManager security = System.getSecurityManager();
+                    if (security != null) {
+                        if (packetAddress.isMulticastAddress()) {
+                            security.checkMulticast(packetAddress, ttl);
+                        } else {
+                            security.checkConnect(packetAddress.getHostAddress(),
+                                    p.getPort());
+                        }
+                    }
+                } else {
+                    // we're connected
+                    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) {
+                        // set the ttl
+                        getImpl().setTTL(ttl);
+                    }
+                    if (p.getPort() == 0) {
+                        throw new SocketException("Can't send to port 0");
+                    }
+                    // call the datagram method to send
+                    getImpl().send(p);
+                } finally {
+                    // set it back to default
+                    if (ttl != dttl) {
+                        getImpl().setTTL(dttl);
+                    }
+                }
+            } // synch p
+        }  //synch ttl
+    } //method
+}
--- a/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java	Fri May 15 10:43:20 2020 -0400
+++ b/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java	Tue May 12 21:51:53 2020 +0100
@@ -74,7 +74,7 @@
     private volatile int timeout;
 
     private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException {
-        super(/*SocketAddress*/null);
+        super(/*SocketAddress*/ DatagramSockets.NO_DELEGATE);
         this.dc = dc;
     }
 
@@ -769,4 +769,24 @@
             }
         }
     }
+
+    /**
+     * Provides access to the value of the private static DatagramSocket.NO_DELEGATE
+     */
+    private static class DatagramSockets {
+        private static final SocketAddress NO_DELEGATE;
+
+        static {
+            try {
+                PrivilegedExceptionAction<Lookup> pa = () ->
+                        MethodHandles.privateLookupIn(DatagramSocket.class, MethodHandles.lookup());
+                MethodHandles.Lookup l = AccessController.doPrivileged(pa);
+                NO_DELEGATE = (SocketAddress)
+                        l.findStaticVarHandle(DatagramSocket.class, "NO_DELEGATE",
+                                SocketAddress.class).get();
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+    }
 }
\ No newline at end of file
--- a/test/jdk/ProblemList.txt	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/ProblemList.txt	Tue May 12 21:51:53 2020 +0100
@@ -627,8 +627,6 @@
 
 java/net/MulticastSocket/SetGetNetworkInterfaceTest.java        8219083 windows-all
 
-java/net/DatagramSocket/SendDatagramToBadAddress.java           7143960 macosx-all
-
 java/net/ServerSocket/AcceptInheritHandle.java                  8211854 aix-ppc64
 
 
@@ -638,8 +636,6 @@
 
 java/nio/Buffer/EqualsCompareTest.java                          8193917 solaris-all
 
-java/nio/channels/DatagramChannel/ChangingAddress.java          7141822 macosx-all
-
 java/nio/channels/DatagramChannel/Unref.java                    8233519 generic-all
 
 java/nio/channels/AsynchronousSocketChannel/StressLoopback.java 8211851 aix-ppc64
--- a/test/jdk/java/net/DatagramSocket/AddressNotSet.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/AddressNotSet.java	Tue May 12 21:51:53 2020 +0100
@@ -27,6 +27,7 @@
  * @summary DatagramSocket.send should throw IllegalArgumentException
  *          when the packet address is not correctly set.
  * @run main AddressNotSet
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl AddressNotSet
  */
 
 import java.net.DatagramPacket;
--- a/test/jdk/java/net/DatagramSocket/B6411513.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/B6411513.java	Tue May 12 21:51:53 2020 +0100
@@ -70,6 +70,9 @@
             System.out.print("disconnect...");
             s.disconnect();
 
+            System.out.println("local addr: " + s.getLocalAddress());
+            System.out.println("local port: " + s.getLocalPort());
+
             byte[] data = { 0, 1, 2 };
             DatagramPacket p = new DatagramPacket(data, data.length,
                     s.getLocalAddress(), s.getLocalPort());
--- a/test/jdk/java/net/DatagramSocket/DatagramTimeout.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/DatagramTimeout.java	Tue May 12 21:51:53 2020 +0100
@@ -27,52 +27,90 @@
  * @summary Test to see if timeout hangs. Also checks that
  * negative timeout value fails as expected.
  * @run testng DatagramTimeout
+ * @run testng/othervm -Djdk.net.usePlainDatagramSocketImpl DatagramTimeout
  */
+
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
+import java.net.MulticastSocket;
+import java.net.SocketException;
 import java.net.SocketTimeoutException;
 import java.nio.channels.DatagramChannel;
 
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
-import static org.testng.Assert.expectThrows;
-
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertThrows;
 
 public class DatagramTimeout {
-    private static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
-    private static final Class<SocketTimeoutException> STE = SocketTimeoutException.class;
+    private static final Class<IllegalArgumentException> IAE =
+            IllegalArgumentException.class;
+    private static final Class<SocketTimeoutException> STE =
+            SocketTimeoutException.class;
+    private static final Class<SocketException> SE = SocketException.class;
 
-    /**
-     * Test DatagramSocket setSoTimeout with a valid timeout value.
-     */
-    @Test
-    public void testSetTimeout() throws Exception {
-        try (DatagramSocket s = new DatagramSocket()) {
-            byte[] buffer = new byte[50];
-            DatagramPacket p = new DatagramPacket(buffer, buffer.length);
-            s.setSoTimeout(2);
-            expectThrows(STE, () -> s.receive(p));
-        }
+    private DatagramSocket datagramSocket, multicastSocket,
+            datagramSocketAdaptor;
+
+    @BeforeTest
+    public void setUp() throws Exception {
+        datagramSocket = new DatagramSocket();
+        multicastSocket = new MulticastSocket();
+        datagramSocketAdaptor = DatagramChannel.open().socket();
     }
 
-    /**
-     * Test DatagramSocket setSoTimeout with a negative timeout.
-     */
-    @Test
-    public void testSetNegativeTimeout() throws Exception {
-        try (DatagramSocket s = new DatagramSocket()) {
-            expectThrows(IAE, () -> s.setSoTimeout(-1));
-        }
+    @DataProvider(name = "data")
+    public Object[][] variants() {
+        return new Object[][]{
+                { datagramSocket        },
+                { datagramSocketAdaptor },
+                { multicastSocket       },
+        };
     }
 
-    /**
-     * Test DatagramSocketAdaptor setSoTimeout with a negative timeout.
-     */
+    @Test(dataProvider = "data")
+    public void testSetNegTimeout(DatagramSocket ds)  {
+        assertThrows(IAE, () -> ds.setSoTimeout(-1));
+    }
+
+    @Test(dataProvider = "data")
+    public void testSetTimeout(DatagramSocket ds) throws Exception {
+        byte[] buffer = new byte[50];
+        DatagramPacket pkt = new DatagramPacket(buffer, buffer.length);
+        ds.setSoTimeout(2);
+        assertThrows(STE, () -> ds.receive(pkt));
+    }
+
+    @Test(dataProvider = "data")
+    public void testGetTimeout(DatagramSocket ds) throws Exception {
+        ds.setSoTimeout(10);
+        assertEquals(10, ds.getSoTimeout());
+    }
+
+    @AfterTest
+    public void tearDown() {
+        datagramSocket.close();
+        multicastSocket.close();
+        datagramSocketAdaptor.close();
+    }
+
     @Test
-    public void testNegativeTimeout() throws Exception {
-        try (DatagramChannel dc = DatagramChannel.open()) {
-            var s = dc.socket();
-            expectThrows(IAE, () -> s.setSoTimeout(-1));
-        }
+    public void testSetGetAfterClose() throws Exception {
+        var ds = new DatagramSocket();
+        var ms = new MulticastSocket();
+        var dsa = DatagramChannel.open().socket();
+
+        ds.close();
+        ms.close();
+        dsa.close();
+        assertThrows(SE, () -> ds.setSoTimeout(10));
+        assertThrows(SE, () -> ds.getSoTimeout());
+        assertThrows(SE, () -> ms.setSoTimeout(10));
+        assertThrows(SE, () -> ms.getSoTimeout());
+        assertThrows(SE, () -> dsa.setSoTimeout(10));
+        assertThrows(SE, () -> dsa.getSoTimeout());
     }
 }
--- a/test/jdk/java/net/DatagramSocket/InterruptibleDatagramSocket.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/InterruptibleDatagramSocket.java	Tue May 12 21:51:53 2020 +0100
@@ -36,6 +36,8 @@
  * @test
  * @summary Check interrupt mechanism for DatagramSocket,
  *      MulticastSocket, and DatagramSocketAdaptor
+ * @run main InterruptibleDatagramSocket
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl InterruptibleDatagramSocket
  */
 
 public class InterruptibleDatagramSocket {
--- a/test/jdk/java/net/DatagramSocket/ReuseAddressTest.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/ReuseAddressTest.java	Tue May 12 21:51:53 2020 +0100
@@ -36,6 +36,7 @@
  * @summary Expected SocketException not thrown when calling bind() with
  *   setReuseAddress(false)
  * @run main/othervm ReuseAddressTest
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl ReuseAddressTest
  */
 
 public class ReuseAddressTest {
--- a/test/jdk/java/net/DatagramSocket/SendCheck.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/SendCheck.java	Tue May 12 21:51:53 2020 +0100
@@ -47,7 +47,8 @@
  *          DatagramSocketAdaptor and DatagramChannel all
  *          throw expected Execption when passed a DatagramPacket
  *          with invalid details
- * @run testng/othervm SendCheck
+ * @run testng SendCheck
+ * @run testng/othervm -Djdk.net.usePlainDatagramSocketImpl SendCheck
  */
 
 public class SendCheck {
--- a/test/jdk/java/net/DatagramSocket/SendPortZero.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/SendPortZero.java	Tue May 12 21:51:53 2020 +0100
@@ -48,7 +48,8 @@
  * @bug 8236105 8240533
  * @summary Check that DatagramSocket throws expected
  *          Exception when sending a DatagramPacket with port 0
- * @run testng/othervm SendPortZero
+ * @run testng SendPortZero
+ * @run testng/othervm -Djdk.net.usePlainDatagramSocketImpl SendPortZero
  */
 
 public class SendPortZero {
--- a/test/jdk/java/net/DatagramSocket/SetGetReceiveBufferSize.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/SetGetReceiveBufferSize.java	Tue May 12 21:51:53 2020 +0100
@@ -27,6 +27,7 @@
  * @summary Check that setReceiveBufferSize and getReceiveBufferSize work as expected
  * @run testng SetGetReceiveBufferSize
  * @run testng/othervm -Djava.net.preferIPv4Stack=true SetGetReceiveBufferSize
+ * @run testng/othervm -Djdk.net.usePlainDatagramSocketImpl SetGetReceiveBufferSize
  */
 
 import org.testng.annotations.DataProvider;
--- a/test/jdk/java/net/DatagramSocket/SetGetSendBufferSize.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/SetGetSendBufferSize.java	Tue May 12 21:51:53 2020 +0100
@@ -29,6 +29,7 @@
  * @summary Check that setSendBufferSize and getSendBufferSize work as expected
  * @run testng SetGetSendBufferSize
  * @run testng/othervm -Djava.net.preferIPv4Stack=true SetGetSendBufferSize
+ * @run testng/othervm -Djdk.net.usePlainDatagramSocketImpl SetGetSendBufferSize
  */
 
 import org.testng.annotations.DataProvider;
--- a/test/jdk/java/net/DatagramSocket/TestAfterClose.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/TestAfterClose.java	Tue May 12 21:51:53 2020 +0100
@@ -25,6 +25,8 @@
  * @test
  * @bug 6505016
  * @summary Socket spec should clarify what getInetAddress/getPort/etc return after the Socket is closed
+ * @run main TestAfterClose
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl TestAfterClose
  */
 
 import java.net.*;
--- a/test/jdk/java/net/DatagramSocket/UnreferencedDatagramSockets.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocket/UnreferencedDatagramSockets.java	Tue May 12 21:51:53 2020 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,10 @@
  * @test
  * @library /test/lib
  * @modules java.management java.base/java.io:+open java.base/java.net:+open
+ *          java.base/sun.net
  * @run main/othervm UnreferencedDatagramSockets
  * @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedDatagramSockets
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl UnreferencedDatagramSockets
  * @summary Check that unreferenced datagram sockets are closed
  */
 
@@ -37,14 +39,18 @@
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.DatagramSocketImpl;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.nio.channels.DatagramChannel;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.ArrayDeque;
 import java.util.List;
 import java.util.Optional;
@@ -54,6 +60,7 @@
 import com.sun.management.UnixOperatingSystemMXBean;
 
 import jdk.test.lib.net.IPSupport;
+import sun.net.NetProperties;
 
 public class UnreferencedDatagramSockets {
 
@@ -185,32 +192,62 @@
                 : -1L;
     }
 
+    private static boolean usePlainDatagramSocketImpl() {
+        PrivilegedAction<String> pa = () -> NetProperties.get("jdk.net.usePlainDatagramSocketImpl");
+        String s = AccessController.doPrivileged(pa);
+        return (s != null) && (s.isEmpty() || s.equalsIgnoreCase("true"));
+    }
+
     // Reflect to find references in the datagram implementation that will be gc'd
     private static void extractRefs(DatagramSocket s, String name) {
         try {
+            Field datagramSocketField = DatagramSocket.class.getDeclaredField("delegate");
+            datagramSocketField.setAccessible(true);
 
-            Field socketImplField = DatagramSocket.class.getDeclaredField("impl");
-            socketImplField.setAccessible(true);
-            Object socketImpl = socketImplField.get(s);
+            if (!usePlainDatagramSocketImpl()) {
+                // DatagramSocket using DatagramSocketAdaptor
+                Object DatagramSocket = datagramSocketField.get(s);
+                assert DatagramSocket.getClass() == Class.forName("sun.nio.ch.DatagramSocketAdaptor");
 
-            Field fileDescriptorField = DatagramSocketImpl.class.getDeclaredField("fd");
-            fileDescriptorField.setAccessible(true);
-            FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);
-            extractRefs(fileDescriptor, name);
+                Method m = DatagramSocket.class.getDeclaredMethod("getChannel");
+                m.setAccessible(true);
+                DatagramChannel datagramChannel = (DatagramChannel) m.invoke(DatagramSocket);
 
-            Class<?> socketImplClass = socketImpl.getClass();
-            System.out.printf("socketImplClass: %s%n", socketImplClass);
-            if (socketImplClass.getName().equals("java.net.TwoStacksPlainDatagramSocketImpl")) {
-                Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
-                fileDescriptor1Field.setAccessible(true);
-                FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(socketImpl);
-                extractRefs(fileDescriptor1, name + "::twoStacksFd1");
+                assert datagramChannel.getClass() == Class.forName("sun.nio.ch.DatagramChannelImpl");
+
+                Field fileDescriptorField = datagramChannel.getClass().getDeclaredField("fd");
+                fileDescriptorField.setAccessible(true);
+                FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(datagramChannel);
+                extractRefs(fileDescriptor, name);
 
             } else {
-                System.out.printf("socketImpl class name not matched: %s != %s%n",
-                        socketImplClass.getName(), "java.net.TwoStacksPlainDatagramSocketImpl");
+                // DatagramSocket using PlainDatagramSocketImpl
+                Object DatagramSocket = datagramSocketField.get(s);
+                assert DatagramSocket.getClass() == Class.forName("java.net.NetMulticastSocket");
+
+                Method m = DatagramSocket.getClass().getDeclaredMethod("getImpl");
+                m.setAccessible(true);
+                DatagramSocketImpl datagramSocketImpl = (DatagramSocketImpl) m.invoke(DatagramSocket);
+
+                Field fileDescriptorField = DatagramSocketImpl.class.getDeclaredField("fd");
+                fileDescriptorField.setAccessible(true);
+                FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(datagramSocketImpl);
+                extractRefs(fileDescriptor, name);
+
+                Class<?> socketImplClass = datagramSocketImpl.getClass();
+                System.out.printf("socketImplClass: %s%n", socketImplClass);
+                if (socketImplClass.getName().equals("java.net.TwoStacksPlainDatagramSocketImpl")) {
+                    Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
+                    fileDescriptor1Field.setAccessible(true);
+                    FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(datagramSocketImpl);
+                    extractRefs(fileDescriptor1, name + "::twoStacksFd1");
+
+                } else {
+                    System.out.printf("socketImpl class name not matched: %s != %s%n",
+                            socketImplClass.getName(), "java.net.TwoStacksPlainDatagramSocketImpl");
+                }
             }
-        } catch (NoSuchFieldException | IllegalAccessException ex) {
+        } catch (Exception ex) {
             ex.printStackTrace();
             throw new AssertionError("missing field", ex);
         }
--- a/test/jdk/java/net/DatagramSocketImpl/TestCreate.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/DatagramSocketImpl/TestCreate.java	Tue May 12 21:51:53 2020 +0100
@@ -60,10 +60,10 @@
         Iterator<CustomDatagramSocketImpl> iterator = List.of(dsi2, dsi3).iterator();
         DatagramSocket.setDatagramSocketImplFactory(() -> iterator.next());
 
-        DatagramSocket ds2 = new DatagramSocket();
+        DatagramSocket ds2 = new DatagramSocket(null);
         assertTrue(dsi2.created.get(), "new DatagramSocket()");
 
-        MulticastSocket ds3 = new MulticastSocket();
+        MulticastSocket ds3 = new MulticastSocket(null);
         assertTrue(dsi3.created.get(), "new MulticastSocket()");
     }
 
--- a/test/jdk/java/net/InetAddress/CheckJNI.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/InetAddress/CheckJNI.java	Tue May 12 21:51:53 2020 +0100
@@ -83,6 +83,10 @@
     static void testDatagram(InetAddress ia) throws Exception {
         DatagramSocket s1 = new DatagramSocket(0, ia);
         DatagramSocket s2 = new DatagramSocket(0, ia);
+        System.out.println("s1: local address=" + s1.getLocalAddress()
+                            + ", local port=" + s1.getLocalPort());
+        System.out.println("s2: local address=" + s2.getLocalAddress()
+                            + ", local port=" + s2.getLocalPort());
 
         DatagramPacket p1 = new DatagramPacket (
                 "hello world".getBytes(),
--- a/test/jdk/java/net/MulticastSocket/B6427403.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/B6427403.java	Tue May 12 21:51:53 2020 +0100
@@ -25,6 +25,8 @@
  * @test
  * @bug 6427403
  * @summary java.net.MulticastSocket.joinGroup() reports 'socket closed'
+ * @run main B6427403
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl B6427403
  */
 import java.net.*;
 import java.io.*;
--- a/test/jdk/java/net/MulticastSocket/MulticastAddresses.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/MulticastAddresses.java	Tue May 12 21:51:53 2020 +0100
@@ -27,6 +27,8 @@
  * @library /test/lib
  * @summary Test that MutlicastSocket.joinGroup is working for
  *          various multicast and non-multicast addresses.
+ * @run main MulticastAddresses
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl MulticastAddresses
  */
 
 import jdk.test.lib.NetworkConfiguration;
--- a/test/jdk/java/net/MulticastSocket/NoSetNetworkInterface.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/NoSetNetworkInterface.java	Tue May 12 21:51:53 2020 +0100
@@ -28,6 +28,7 @@
  * @run main/othervm NoSetNetworkInterface
  * @run main/othervm -Djava.net.preferIPv4Stack=true NoSetNetworkInterface
  * @run main/othervm -Djava.net.preferIPv6Addresses=true NoSetNetworkInterface
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl NoSetNetworkInterface
  * @summary Check that methods that are used to set and get the NetworkInterface
  *  for a MulticastSocket work as expected. This test also checks that getOption
  *  returns null correctly when a NetworkInterface has not been set
--- a/test/jdk/java/net/MulticastSocket/Promiscuous.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/Promiscuous.java	Tue May 12 21:51:53 2020 +0100
@@ -26,6 +26,7 @@
  * @library /test/lib
  * @summary Test for interference when two sockets are bound to the same
  *          port but joined to different multicast groups
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl Promiscuous
  * @run main Promiscuous
  * @run main/othervm -Djava.net.preferIPv4Stack=true Promiscuous
  */
--- a/test/jdk/java/net/MulticastSocket/SendPortZero.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/SendPortZero.java	Tue May 12 21:51:53 2020 +0100
@@ -48,7 +48,8 @@
  * @bug 8243408
  * @summary Check that MulticastSocket throws expected
  *          Exception when sending a DatagramPacket with port 0
- * @run testng/othervm SendPortZero
+ * @run testng SendPortZero
+ * @run testng/othervm -Djdk.net.usePlainDatagramSocketImpl SendPortZero
  */
 
 public class SendPortZero {
--- a/test/jdk/java/net/MulticastSocket/SetLoopbackMode.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/SetLoopbackMode.java	Tue May 12 21:51:53 2020 +0100
@@ -30,6 +30,7 @@
  * @build jdk.test.lib.NetworkConfiguration
  *        jdk.test.lib.Platform
  * @run main/othervm SetLoopbackMode
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl SetLoopbackMode
  */
 
 import java.lang.reflect.Method;
@@ -53,6 +54,8 @@
             System.out.println("Loopback mode is enabled.");
         }
 
+        System.out.println(mc.getLocalSocketAddress());
+
         byte b[] = "hello".getBytes();
         DatagramPacket p = new DatagramPacket(b, b.length, grp,
                                 mc.getLocalPort());
--- a/test/jdk/java/net/MulticastSocket/SetLoopbackModeIPv4.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/SetLoopbackModeIPv4.java	Tue May 12 21:51:53 2020 +0100
@@ -32,6 +32,8 @@
  *        SetLoopbackMode
  *        SetLoopbackModeIPv4
  * @run main/othervm -Djava.net.preferIPv4Stack=true SetLoopbackModeIPv4
+ * @run main/othervm -Djava.net.preferIPv4Stack=true
+ *                   -Djdk.net.usePlainDatagramSocketImpl SetLoopbackModeIPv4
  */
 
 import jdk.test.lib.net.IPSupport;
--- a/test/jdk/java/net/MulticastSocket/SetLoopbackOption.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/SetLoopbackOption.java	Tue May 12 21:51:53 2020 +0100
@@ -31,6 +31,7 @@
  * @run testng/othervm SetLoopbackOption
  * @run testng/othervm -Djava.net.preferIPv4Stack=true SetLoopbackOption
  * @run testng/othervm -Djava.net.preferIPv6Addresses=true SetLoopbackOption
+ * @run testng/othervm -Djdk.net.usePlainDatagramSocketImpl SetLoopbackOption
  */
 
 import java.io.FileDescriptor;
--- a/test/jdk/java/net/MulticastSocket/SetOutgoingIf.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/SetOutgoingIf.java	Tue May 12 21:51:53 2020 +0100
@@ -26,6 +26,7 @@
  * @bug 4742177 8241786
  * @library /test/lib
  * @run main/othervm SetOutgoingIf
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl SetOutgoingIf
  * @summary Re-test IPv6 (and specifically MulticastSocket) with latest Linux & USAGI code
  */
 import java.io.IOException;
--- a/test/jdk/java/net/MulticastSocket/SetTTLAndGetTTL.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/SetTTLAndGetTTL.java	Tue May 12 21:51:53 2020 +0100
@@ -24,6 +24,8 @@
 /* @test
  * @bug 4189640
  * @summary Make setTTL/getTTL works
+ * @run main SetTTLAndGetTTL
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl SetTTLAndGetTTL
  */
 
 import java.net.*;
--- a/test/jdk/java/net/MulticastSocket/SetTTLTo0.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/SetTTLTo0.java	Tue May 12 21:51:53 2020 +0100
@@ -24,6 +24,8 @@
 /* @test
  * @bug 4148757
  * @summary Make sure TTL can be set to 0
+ * @run main SetTTLTo0
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl SetTTLTo0
  */
 
 import java.net.*;
--- a/test/jdk/java/net/MulticastSocket/UnreferencedMulticastSockets.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/MulticastSocket/UnreferencedMulticastSockets.java	Tue May 12 21:51:53 2020 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,9 @@
  * @test
  * @library /test/lib
  * @modules java.management java.base/java.io:+open java.base/java.net:+open
+ *          java.base/sun.net
  * @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedMulticastSockets
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl UnreferencedMulticastSockets
  * @run main/othervm UnreferencedMulticastSockets
  * @summary Check that unreferenced multicast sockets are closed
  */
@@ -37,6 +39,7 @@
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.DatagramSocketImpl;
@@ -44,9 +47,12 @@
 import java.net.InetSocketAddress;
 import java.net.MulticastSocket;
 import java.net.UnknownHostException;
+import java.nio.channels.DatagramChannel;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.ArrayDeque;
 import java.util.List;
 import java.util.Optional;
@@ -56,6 +62,7 @@
 import jdk.test.lib.net.IPSupport;
 
 import com.sun.management.UnixOperatingSystemMXBean;
+import sun.net.NetProperties;
 
 public class UnreferencedMulticastSockets {
 
@@ -223,32 +230,62 @@
                 : -1L;
     }
 
-    // Reflect to find references in the socket implementation that will be gc'd
-    private static void extractRefs(MulticastSocket s, String name) {
+    private static boolean usePlainDatagramSocketImpl() {
+        PrivilegedAction<String> pa = () -> NetProperties.get("jdk.net.usePlainDatagramSocketImpl");
+        String s = AccessController.doPrivileged(pa);
+        return (s != null) && (s.isEmpty() || s.equalsIgnoreCase("true"));
+    }
+
+    // Reflect to find references in the datagram implementation that will be gc'd
+    private static void extractRefs(DatagramSocket s, String name) {
+
         try {
+            Field datagramSocketField = DatagramSocket.class.getDeclaredField("delegate");
+            datagramSocketField.setAccessible(true);
 
-            Field socketImplField = DatagramSocket.class.getDeclaredField("impl");
-            socketImplField.setAccessible(true);
-            Object socketImpl = socketImplField.get(s);
+            if (!usePlainDatagramSocketImpl()) {
+                // MulticastSocket using DatagramSocketAdaptor
+                Object MulticastSocket = datagramSocketField.get(s);
 
-            Field fileDescriptorField = DatagramSocketImpl.class.getDeclaredField("fd");
-            fileDescriptorField.setAccessible(true);
-            FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);
-            extractRefs(fileDescriptor, name);
+                Method m = DatagramSocket.class.getDeclaredMethod("getChannel");
+                m.setAccessible(true);
+                DatagramChannel datagramChannel = (DatagramChannel) m.invoke(MulticastSocket);
 
-            Class<?> socketImplClass = socketImpl.getClass();
-            System.out.printf("socketImplClass: %s%n", socketImplClass);
-            if (socketImplClass.getName().equals("java.net.TwoStacksPlainDatagramSocketImpl")) {
-                Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
-                fileDescriptor1Field.setAccessible(true);
-                FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(socketImpl);
-                extractRefs(fileDescriptor1, name + "::twoStacksFd1");
+                assert datagramChannel.getClass() == Class.forName("sun.nio.ch.DatagramChannelImpl");
+
+                Field fileDescriptorField = datagramChannel.getClass().getDeclaredField("fd");
+                fileDescriptorField.setAccessible(true);
+                FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(datagramChannel);
+                extractRefs(fileDescriptor, name);
 
             } else {
-                System.out.printf("socketImpl class name not matched: %s != %s%n",
-                        socketImplClass.getName(), "java.net.TwoStacksPlainDatagramSocketImpl");
+                // MulticastSocket using PlainDatagramSocketImpl
+                Object MulticastSocket = datagramSocketField.get(s);
+                assert MulticastSocket.getClass() == Class.forName("java.net.NetMulticastSocket");
+
+                Method m = MulticastSocket.getClass().getDeclaredMethod("getImpl");
+                m.setAccessible(true);
+                DatagramSocketImpl datagramSocketImpl = (DatagramSocketImpl) m.invoke(MulticastSocket);
+
+                Field fileDescriptorField = DatagramSocketImpl.class.getDeclaredField("fd");
+                fileDescriptorField.setAccessible(true);
+                FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(datagramSocketImpl);
+                extractRefs(fileDescriptor, name);
+
+                Class<?> socketImplClass = datagramSocketImpl.getClass();
+                System.out.printf("socketImplClass: %s%n", socketImplClass);
+                if (socketImplClass.getName().equals("java.net.TwoStacksPlainDatagramSocketImpl")) {
+                    Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
+                    fileDescriptor1Field.setAccessible(true);
+                    FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(datagramSocketImpl);
+                    extractRefs(fileDescriptor1, name + "::twoStacksFd1");
+
+                } else {
+                    System.out.printf("socketImpl class name not matched: %s != %s%n",
+                            socketImplClass.getName(), "java.net.TwoStacksPlainDatagramSocketImpl");
+                }
             }
-        } catch (NoSuchFieldException | IllegalAccessException ex) {
+        } catch (Exception ex) {
             ex.printStackTrace();
             throw new AssertionError("missing field", ex);
         }
--- a/test/jdk/java/net/Socket/AddressTest.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/Socket/AddressTest.java	Tue May 12 21:51:53 2020 +0100
@@ -29,6 +29,7 @@
  *          SocketAddress
  * @run main AddressTest
  * @run main/othervm -Djava.net.preferIPv4Stack=true AddressTest
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl AddressTest
  */
 
 import java.net.*;
--- a/test/jdk/java/net/SocketOption/AfterClose.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/SocketOption/AfterClose.java	Tue May 12 21:51:53 2020 +0100
@@ -27,6 +27,7 @@
  * @summary Ensures that IOException is thrown after the socket is closed
  * @run testng AfterClose
  * @run testng/othervm -Djdk.net.usePlainSocketImpl AfterClose
+ * @run testng/othervm -Djdk.net.usePlainDatagramSocketImpl AfterClose
  */
 
 import java.io.IOException;
@@ -48,6 +49,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 import static java.lang.Boolean.*;
--- a/test/jdk/java/net/SocketOption/OptionsTest.java	Fri May 15 10:43:20 2020 -0400
+++ b/test/jdk/java/net/SocketOption/OptionsTest.java	Tue May 12 21:51:53 2020 +0100
@@ -28,6 +28,7 @@
  * @requires !vm.graal.enabled
  * @run main/othervm -Xcheck:jni OptionsTest
  * @run main/othervm -Djdk.net.usePlainSocketImpl OptionsTest
+ * @run main/othervm -Djdk.net.usePlainDatagramSocketImpl OptionsTest
  * @run main/othervm -Xcheck:jni -Djava.net.preferIPv4Stack=true OptionsTest
  * @run main/othervm --limit-modules=java.base OptionsTest
  */