changeset 54554:9366628d727b

8216986: Remove unused code from SocksSocketImpl Reviewed-by: alanb
author michaelm
date Thu, 24 Jan 2019 15:48:05 +0000
parents cb43e14dc68b
children 1262a93634c2
files src/java.base/share/classes/java/net/HttpConnectSocketImpl.java src/java.base/share/classes/java/net/SocksSocketImpl.java test/jdk/java/net/Socks/SocksServer.java test/jdk/java/net/Socks/SocksV4Test.java test/jdk/sun/net/www/protocol/http/ProxyTunnelServer.java test/jdk/sun/net/www/protocol/http/TunnelThroughProxy.java
diffstat 6 files changed, 148 insertions(+), 591 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/net/HttpConnectSocketImpl.java	Tue Jan 22 15:33:34 2019 +0800
+++ b/src/java.base/share/classes/java/net/HttpConnectSocketImpl.java	Thu Jan 24 15:48:05 2019 +0000
@@ -197,14 +197,4 @@
         else
             return super.getPort();
     }
-
-    @Override
-    protected int getLocalPort() {
-        if (socket != null)
-            return super.getLocalPort();
-        if (external_address != null)
-            return external_address.getPort();
-        else
-            return super.getLocalPort();
-    }
 }
--- a/src/java.base/share/classes/java/net/SocksSocketImpl.java	Tue Jan 22 15:33:34 2019 +0800
+++ b/src/java.base/share/classes/java/net/SocksSocketImpl.java	Thu Jan 24 15:48:05 2019 +0000
@@ -28,14 +28,11 @@
 import java.io.OutputStream;
 import java.io.BufferedOutputStream;
 import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedExceptionAction;
 
 import jdk.internal.util.StaticProperty;
 import sun.net.SocksProxy;
 import sun.net.spi.DefaultProxySelector;
 import sun.net.www.ParseUtil;
-/* import org.ietf.jgss.*; */
 
 /**
  * SOCKS (V4 & V5) TCP socket implementation (RFC 1928).
@@ -51,19 +48,11 @@
     private Socket cmdsock = null;
     private InputStream cmdIn = null;
     private OutputStream cmdOut = null;
-    /* true if the Proxy has been set programmatically */
-    private boolean applicationSetProxy;  /* false */
-
 
     SocksSocketImpl() {
         // Nothing needed
     }
 
-    SocksSocketImpl(String server, int port) {
-        this.server = server;
-        this.serverPort = (port == -1 ? DEFAULT_PORT : port);
-    }
-
     SocksSocketImpl(Proxy proxy) {
         SocketAddress a = proxy.address();
         if (a instanceof InetSocketAddress) {
@@ -75,10 +64,6 @@
         useV4 = useV4(proxy);
     }
 
-    void setV4() {
-        useV4 = true;
-    }
-
     private static boolean useV4(Proxy proxy) {
         if (proxy instanceof SocksProxy
             && ((SocksProxy)proxy).protocolVersion() == 4) {
@@ -123,10 +108,6 @@
         throw new SocketTimeoutException();
     }
 
-    private int readSocksReply(InputStream in, byte[] data) throws IOException {
-        return readSocksReply(in, data, 0L);
-    }
-
     private int readSocksReply(InputStream in, byte[] data, long deadlineMillis) throws IOException {
         int len = data.length;
         int received = 0;
@@ -144,14 +125,6 @@
         return received;
     }
 
-    /**
-     * Provides the authentication mechanism required by the proxy.
-     */
-    private boolean authenticate(byte method, InputStream in,
-                                 BufferedOutputStream out) throws IOException {
-        return authenticate(method, in, out, 0L);
-    }
-
     private boolean authenticate(byte method, InputStream in,
                                  BufferedOutputStream out,
                                  long deadlineMillis) throws IOException {
@@ -212,60 +185,6 @@
             /* Authentication succeeded */
             return true;
         }
-        /**
-         * GSSAPI authentication mechanism.
-         * Unfortunately the RFC seems out of sync with the Reference
-         * implementation. I'll leave this in for future completion.
-         */
-//      if (method == GSSAPI) {
-//          try {
-//              GSSManager manager = GSSManager.getInstance();
-//              GSSName name = manager.createName("SERVICE:socks@"+server,
-//                                                   null);
-//              GSSContext context = manager.createContext(name, null, null,
-//                                                         GSSContext.DEFAULT_LIFETIME);
-//              context.requestMutualAuth(true);
-//              context.requestReplayDet(true);
-//              context.requestSequenceDet(true);
-//              context.requestCredDeleg(true);
-//              byte []inToken = new byte[0];
-//              while (!context.isEstablished()) {
-//                  byte[] outToken
-//                      = context.initSecContext(inToken, 0, inToken.length);
-//                  // send the output token if generated
-//                  if (outToken != null) {
-//                      out.write(1);
-//                      out.write(1);
-//                      out.writeShort(outToken.length);
-//                      out.write(outToken);
-//                      out.flush();
-//                      data = new byte[2];
-//                      i = readSocksReply(in, data, deadlineMillis);
-//                      if (i != 2 || data[1] == 0xff) {
-//                          in.close();
-//                          out.close();
-//                          return false;
-//                      }
-//                      i = readSocksReply(in, data, deadlineMillis);
-//                      int len = 0;
-//                      len = ((int)data[0] & 0xff) << 8;
-//                      len += data[1];
-//                      data = new byte[len];
-//                      i = readSocksReply(in, data, deadlineMillis);
-//                      if (i == len)
-//                          return true;
-//                      in.close();
-//                      out.close();
-//                  }
-//              }
-//          } catch (GSSException e) {
-//              /* RFC 1961 states that if Context initialisation fails the connection
-//                 MUST be closed */
-//              e.printStackTrace();
-//              in.close();
-//              out.close();
-//          }
-//      }
         return false;
     }
 
@@ -590,450 +509,6 @@
         external_address = epoint;
     }
 
-    private void bindV4(InputStream in, OutputStream out,
-                        InetAddress baddr,
-                        int lport) throws IOException {
-        if (!(baddr instanceof Inet4Address)) {
-            throw new SocketException("SOCKS V4 requires IPv4 only addresses");
-        }
-        super.bind(baddr, lport);
-        byte[] addr1 = baddr.getAddress();
-        /* Test for AnyLocal */
-        InetAddress naddr = baddr;
-        if (naddr.isAnyLocalAddress()) {
-            naddr = AccessController.doPrivileged(
-                        new PrivilegedAction<>() {
-                            public InetAddress run() {
-                                return cmdsock.getLocalAddress();
-
-                            }
-                        });
-            addr1 = naddr.getAddress();
-        }
-        out.write(PROTO_VERS4);
-        out.write(BIND);
-        out.write((super.getLocalPort() >> 8) & 0xff);
-        out.write((super.getLocalPort() >> 0) & 0xff);
-        out.write(addr1);
-        String userName = getUserName();
-        try {
-            out.write(userName.getBytes("ISO-8859-1"));
-        } catch (java.io.UnsupportedEncodingException uee) {
-            assert false;
-        }
-        out.write(0);
-        out.flush();
-        byte[] data = new byte[8];
-        int n = readSocksReply(in, data);
-        if (n != 8)
-            throw new SocketException("Reply from SOCKS server has bad length: " + n);
-        if (data[0] != 0 && data[0] != 4)
-            throw new SocketException("Reply from SOCKS server has bad version");
-        SocketException ex = null;
-        switch (data[1]) {
-        case 90:
-            // Success!
-            external_address = new InetSocketAddress(baddr, lport);
-            break;
-        case 91:
-            ex = new SocketException("SOCKS request rejected");
-            break;
-        case 92:
-            ex = new SocketException("SOCKS server couldn't reach destination");
-            break;
-        case 93:
-            ex = new SocketException("SOCKS authentication failed");
-            break;
-        default:
-            ex = new SocketException("Reply from SOCKS server contains bad status");
-            break;
-        }
-        if (ex != null) {
-            in.close();
-            out.close();
-            throw ex;
-        }
-
-    }
-
-    /**
-     * Sends the Bind request to the SOCKS proxy. In the SOCKS protocol, bind
-     * means "accept incoming connection from", so the SocketAddress is
-     * the one of the host we do accept connection from.
-     *
-     * @param      saddr   the Socket address of the remote host.
-     * @exception  IOException  if an I/O error occurs when binding this socket.
-     */
-    protected synchronized void socksBind(InetSocketAddress saddr) throws IOException {
-        if (socket != null) {
-            // this is a client socket, not a server socket, don't
-            // call the SOCKS proxy for a bind!
-            return;
-        }
-
-        // Connects to the SOCKS server
-
-        if (server == null) {
-            // This is the general case
-            // server is not null only when the socket was created with a
-            // specified proxy in which case it does bypass the ProxySelector
-            ProxySelector sel = java.security.AccessController.doPrivileged(
-                new java.security.PrivilegedAction<>() {
-                    public ProxySelector run() {
-                            return ProxySelector.getDefault();
-                        }
-                    });
-            if (sel == null) {
-                /*
-                 * No default proxySelector --> direct connection
-                 */
-                return;
-            }
-            URI uri;
-            // Use getHostString() to avoid reverse lookups
-            String host = saddr.getHostString();
-            // IPv6 literal?
-            if (saddr.getAddress() instanceof Inet6Address &&
-                (!host.startsWith("[")) && (host.indexOf(':') >= 0)) {
-                host = "[" + host + "]";
-            }
-            try {
-                uri = new URI("serversocket://" + ParseUtil.encodePath(host) + ":"+ saddr.getPort());
-            } catch (URISyntaxException e) {
-                // This shouldn't happen
-                assert false : e;
-                uri = null;
-            }
-            Proxy p = null;
-            Exception savedExc = null;
-            java.util.Iterator<Proxy> iProxy = null;
-            iProxy = sel.select(uri).iterator();
-            if (iProxy == null || !(iProxy.hasNext())) {
-                return;
-            }
-            while (iProxy.hasNext()) {
-                p = iProxy.next();
-                if (p == null || p.type() != Proxy.Type.SOCKS) {
-                    return;
-                }
-
-                if (!(p.address() instanceof InetSocketAddress))
-                    throw new SocketException("Unknown address type for proxy: " + p);
-                // Use getHostString() to avoid reverse lookups
-                server = ((InetSocketAddress) p.address()).getHostString();
-                serverPort = ((InetSocketAddress) p.address()).getPort();
-                useV4 = useV4(p);
-
-                // Connects to the SOCKS server
-                try {
-                    AccessController.doPrivileged(
-                        new PrivilegedExceptionAction<>() {
-                            public Void run() throws Exception {
-                                cmdsock = new Socket(new PlainSocketImpl());
-                                cmdsock.connect(new InetSocketAddress(server, serverPort));
-                                cmdIn = cmdsock.getInputStream();
-                                cmdOut = cmdsock.getOutputStream();
-                                return null;
-                            }
-                        });
-                } catch (Exception e) {
-                    // Ooops, let's notify the ProxySelector
-                    sel.connectFailed(uri,p.address(),new SocketException(e.getMessage()));
-                    server = null;
-                    serverPort = -1;
-                    cmdsock = null;
-                    savedExc = e;
-                    // Will continue the while loop and try the next proxy
-                }
-            }
-
-            /*
-             * If server is still null at this point, none of the proxy
-             * worked
-             */
-            if (server == null || cmdsock == null) {
-                throw new SocketException("Can't connect to SOCKS proxy:"
-                                          + savedExc.getMessage());
-            }
-        } else {
-            try {
-                AccessController.doPrivileged(
-                    new PrivilegedExceptionAction<>() {
-                        public Void run() throws Exception {
-                            cmdsock = new Socket(new PlainSocketImpl());
-                            cmdsock.connect(new InetSocketAddress(server, serverPort));
-                            cmdIn = cmdsock.getInputStream();
-                            cmdOut = cmdsock.getOutputStream();
-                            return null;
-                        }
-                    });
-            } catch (Exception e) {
-                throw new SocketException(e.getMessage());
-            }
-        }
-        BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
-        InputStream in = cmdIn;
-        if (useV4) {
-            bindV4(in, out, saddr.getAddress(), saddr.getPort());
-            return;
-        }
-        out.write(PROTO_VERS);
-        out.write(2);
-        out.write(NO_AUTH);
-        out.write(USER_PASSW);
-        out.flush();
-        byte[] data = new byte[2];
-        int i = readSocksReply(in, data);
-        if (i != 2 || ((int)data[0]) != PROTO_VERS) {
-            // Maybe it's not a V5 sever after all
-            // Let's try V4 before we give up
-            bindV4(in, out, saddr.getAddress(), saddr.getPort());
-            return;
-        }
-        if (((int)data[1]) == NO_METHODS)
-            throw new SocketException("SOCKS : No acceptable methods");
-        if (!authenticate(data[1], in, out)) {
-            throw new SocketException("SOCKS : authentication failed");
-        }
-        // We're OK. Let's issue the BIND command.
-        out.write(PROTO_VERS);
-        out.write(BIND);
-        out.write(0);
-        int lport = saddr.getPort();
-        if (saddr.isUnresolved()) {
-            out.write(DOMAIN_NAME);
-            out.write(saddr.getHostName().length());
-            try {
-                out.write(saddr.getHostName().getBytes("ISO-8859-1"));
-            } catch (java.io.UnsupportedEncodingException uee) {
-                assert false;
-            }
-            out.write((lport >> 8) & 0xff);
-            out.write((lport >> 0) & 0xff);
-        } else if (saddr.getAddress() instanceof Inet4Address) {
-            byte[] addr1 = saddr.getAddress().getAddress();
-            out.write(IPV4);
-            out.write(addr1);
-            out.write((lport >> 8) & 0xff);
-            out.write((lport >> 0) & 0xff);
-            out.flush();
-        } else if (saddr.getAddress() instanceof Inet6Address) {
-            byte[] addr1 = saddr.getAddress().getAddress();
-            out.write(IPV6);
-            out.write(addr1);
-            out.write((lport >> 8) & 0xff);
-            out.write((lport >> 0) & 0xff);
-            out.flush();
-        } else {
-            cmdsock.close();
-            throw new SocketException("unsupported address type : " + saddr);
-        }
-        data = new byte[4];
-        i = readSocksReply(in, data);
-        SocketException ex = null;
-        int len, nport;
-        byte[] addr;
-        switch (data[1]) {
-        case REQUEST_OK:
-            // success!
-            switch(data[3]) {
-            case IPV4:
-                addr = new byte[4];
-                i = readSocksReply(in, addr);
-                if (i != 4)
-                    throw new SocketException("Reply from SOCKS server badly formatted");
-                data = new byte[2];
-                i = readSocksReply(in, data);
-                if (i != 2)
-                    throw new SocketException("Reply from SOCKS server badly formatted");
-                nport = ((int)data[0] & 0xff) << 8;
-                nport += ((int)data[1] & 0xff);
-                external_address =
-                    new InetSocketAddress(new Inet4Address("", addr) , nport);
-                break;
-            case DOMAIN_NAME:
-                len = data[1];
-                byte[] host = new byte[len];
-                i = readSocksReply(in, host);
-                if (i != len)
-                    throw new SocketException("Reply from SOCKS server badly formatted");
-                data = new byte[2];
-                i = readSocksReply(in, data);
-                if (i != 2)
-                    throw new SocketException("Reply from SOCKS server badly formatted");
-                nport = ((int)data[0] & 0xff) << 8;
-                nport += ((int)data[1] & 0xff);
-                external_address = new InetSocketAddress(new String(host), nport);
-                break;
-            case IPV6:
-                len = data[1];
-                addr = new byte[len];
-                i = readSocksReply(in, addr);
-                if (i != len)
-                    throw new SocketException("Reply from SOCKS server badly formatted");
-                data = new byte[2];
-                i = readSocksReply(in, data);
-                if (i != 2)
-                    throw new SocketException("Reply from SOCKS server badly formatted");
-                nport = ((int)data[0] & 0xff) << 8;
-                nport += ((int)data[1] & 0xff);
-                external_address =
-                    new InetSocketAddress(new Inet6Address("", addr), nport);
-                break;
-            }
-            break;
-        case GENERAL_FAILURE:
-            ex = new SocketException("SOCKS server general failure");
-            break;
-        case NOT_ALLOWED:
-            ex = new SocketException("SOCKS: Bind not allowed by ruleset");
-            break;
-        case NET_UNREACHABLE:
-            ex = new SocketException("SOCKS: Network unreachable");
-            break;
-        case HOST_UNREACHABLE:
-            ex = new SocketException("SOCKS: Host unreachable");
-            break;
-        case CONN_REFUSED:
-            ex = new SocketException("SOCKS: Connection refused");
-            break;
-        case TTL_EXPIRED:
-            ex =  new SocketException("SOCKS: TTL expired");
-            break;
-        case CMD_NOT_SUPPORTED:
-            ex = new SocketException("SOCKS: Command not supported");
-            break;
-        case ADDR_TYPE_NOT_SUP:
-            ex = new SocketException("SOCKS: address type not supported");
-            break;
-        }
-        if (ex != null) {
-            in.close();
-            out.close();
-            cmdsock.close();
-            cmdsock = null;
-            throw ex;
-        }
-        cmdIn = in;
-        cmdOut = out;
-    }
-
-    /**
-     * Accepts a connection from a specific host.
-     *
-     * @param      s   the accepted connection.
-     * @param      saddr the socket address of the host we do accept
-     *               connection from
-     * @exception  IOException  if an I/O error occurs when accepting the
-     *               connection.
-     */
-    protected void acceptFrom(SocketImpl s, InetSocketAddress saddr) throws IOException {
-        if (cmdsock == null) {
-            // Not a Socks ServerSocket.
-            return;
-        }
-        InputStream in = cmdIn;
-        // Sends the "SOCKS BIND" request.
-        socksBind(saddr);
-        in.read();
-        int i = in.read();
-        in.read();
-        SocketException ex = null;
-        int nport;
-        byte[] addr;
-        InetSocketAddress real_end = null;
-        switch (i) {
-        case REQUEST_OK:
-            // success!
-            i = in.read();
-            switch(i) {
-            case IPV4:
-                addr = new byte[4];
-                readSocksReply(in, addr);
-                nport = in.read() << 8;
-                nport += in.read();
-                real_end =
-                    new InetSocketAddress(new Inet4Address("", addr) , nport);
-                break;
-            case DOMAIN_NAME:
-                int len = in.read();
-                addr = new byte[len];
-                readSocksReply(in, addr);
-                nport = in.read() << 8;
-                nport += in.read();
-                real_end = new InetSocketAddress(new String(addr), nport);
-                break;
-            case IPV6:
-                addr = new byte[16];
-                readSocksReply(in, addr);
-                nport = in.read() << 8;
-                nport += in.read();
-                real_end =
-                    new InetSocketAddress(new Inet6Address("", addr), nport);
-                break;
-            }
-            break;
-        case GENERAL_FAILURE:
-            ex = new SocketException("SOCKS server general failure");
-            break;
-        case NOT_ALLOWED:
-            ex = new SocketException("SOCKS: Accept not allowed by ruleset");
-            break;
-        case NET_UNREACHABLE:
-            ex = new SocketException("SOCKS: Network unreachable");
-            break;
-        case HOST_UNREACHABLE:
-            ex = new SocketException("SOCKS: Host unreachable");
-            break;
-        case CONN_REFUSED:
-            ex = new SocketException("SOCKS: Connection refused");
-            break;
-        case TTL_EXPIRED:
-            ex =  new SocketException("SOCKS: TTL expired");
-            break;
-        case CMD_NOT_SUPPORTED:
-            ex = new SocketException("SOCKS: Command not supported");
-            break;
-        case ADDR_TYPE_NOT_SUP:
-            ex = new SocketException("SOCKS: address type not supported");
-            break;
-        }
-        if (ex != null) {
-            cmdIn.close();
-            cmdOut.close();
-            cmdsock.close();
-            cmdsock = null;
-            throw ex;
-        }
-
-        /**
-         * This is where we have to do some fancy stuff.
-         * The datastream from the socket "accepted" by the proxy will
-         * come through the cmdSocket. So we have to swap the socketImpls
-         */
-        if (s instanceof SocksSocketImpl) {
-            ((SocksSocketImpl)s).external_address = real_end;
-        }
-        if (s instanceof PlainSocketImpl) {
-            PlainSocketImpl psi = (PlainSocketImpl) s;
-            psi.setInputStream((SocketInputStream) in);
-            psi.setFileDescriptor(cmdsock.getImpl().getFileDescriptor());
-            psi.setAddress(cmdsock.getImpl().getInetAddress());
-            psi.setPort(cmdsock.getImpl().getPort());
-            psi.setLocalPort(cmdsock.getImpl().getLocalPort());
-        } else {
-            s.fd = cmdsock.getImpl().fd;
-            s.address = cmdsock.getImpl().address;
-            s.port = cmdsock.getImpl().port;
-            s.localport = cmdsock.getImpl().localport;
-        }
-
-        // Need to do that so that the socket won't be closed
-        // when the ServerSocket is closed by the user.
-        // It kinds of detaches the Socket because it is now
-        // used elsewhere.
-        cmdsock = null;
-    }
 
 
     /**
@@ -1065,16 +540,6 @@
     }
 
     @Override
-    protected int getLocalPort() {
-        if (socket != null)
-            return super.getLocalPort();
-        if (external_address != null)
-            return external_address.getPort();
-        else
-            return super.getLocalPort();
-    }
-
-    @Override
     protected void close() throws IOException {
         if (cmdsock != null)
             cmdsock.close();
@@ -1083,14 +548,6 @@
     }
 
     private String getUserName() {
-        String userName = "";
-        if (applicationSetProxy) {
-            try {
-                userName = System.getProperty("user.name");
-            } catch (SecurityException se) { /* swallow Exception */ }
-        } else {
-            userName = StaticProperty.userName();
-        }
-        return userName;
+        return StaticProperty.userName();
     }
 }
--- a/test/jdk/java/net/Socks/SocksServer.java	Tue Jan 22 15:33:34 2019 +0800
+++ b/test/jdk/java/net/Socks/SocksServer.java	Thu Jan 24 15:48:05 2019 +0000
@@ -24,7 +24,7 @@
 import java.io.*;
 import java.util.HashMap;
 
-public class SocksServer extends Thread {
+public class SocksServer extends Thread implements Closeable {
     // Some useful SOCKS constant
 
     static final int PROTO_VERS4        = 4;
@@ -503,7 +503,7 @@
         return port;
     }
 
-    public void terminate() {
+    public void close() {
         done = true;
         try { server.close(); } catch (IOException unused) {}
     }
--- a/test/jdk/java/net/Socks/SocksV4Test.java	Tue Jan 22 15:33:34 2019 +0800
+++ b/test/jdk/java/net/Socks/SocksV4Test.java	Thu Jan 24 15:48:05 2019 +0000
@@ -26,36 +26,80 @@
  * @bug 4727547
  * @summary SocksSocketImpl throws NullPointerException
  * @build SocksServer
- * @run main SocksV4Test
+ * @run main/othervm SocksV4Test
  */
 
+import java.io.IOException;
 import java.net.*;
 
 public class SocksV4Test {
 
     // An unresolvable host
     static final String HOSTNAME = "doesnot.exist.invalid";
+    static final String USER = "johndoe";
+    static final String PASSWORD = "helloworld";
 
     public static void main(String[] args) throws Exception {
+        Authenticator.setDefault(new Auth());
+        UHETest();
+        getLocalPortTest();
+    }
+
+    static class Auth extends Authenticator {
+        protected PasswordAuthentication getPasswordAuthentication() {
+            return new PasswordAuthentication(USER, PASSWORD.toCharArray());
+        }
+    }
+
+    public static void getLocalPortTest() throws Exception {
+        // We actually use V5 for this test because that is the default
+        // protocol version used by the client and it doesn't really handle
+        // down grading very well.
+        try (SocksServer srvr = new SocksServer(0, false);
+             ServerSocket ss = new ServerSocket(0)) {
+
+            srvr.addUser(USER, PASSWORD);
+            int serverPort = ss.getLocalPort();
+            srvr.start();
+            int proxyPort = srvr.getPort();
+            System.out.printf("Server port %d, Proxy port %d\n", serverPort, proxyPort);
+            Proxy sp = new Proxy(Proxy.Type.SOCKS,
+                    new InetSocketAddress("localhost", proxyPort));
+            // Let's create an unresolved address
+            InetSocketAddress ad = new InetSocketAddress("127.0.0.1", serverPort);
+            try (Socket s = new Socket(sp)) {
+                s.connect(ad, 10000);
+                int pp = s.getLocalPort();
+                System.out.println("Local port = " + pp);
+                if (pp == serverPort || pp == proxyPort)
+                    throw new RuntimeException("wrong port returned");
+            } catch (UnknownHostException ex) {
+                throw new RuntimeException(ex);
+            } catch (IOException ioe) {
+                throw new RuntimeException(ioe);
+            }
+        }
+    }
+
+    public static void UHETest() throws Exception {
         // sanity before running the test
         assertUnresolvableHost(HOSTNAME);
 
         // Create a SOCKS V4 proxy
-        SocksServer srvr = new SocksServer(0, true);
-        srvr.start();
-        Proxy sp = new Proxy(Proxy.Type.SOCKS,
-                             new InetSocketAddress("localhost", srvr.getPort()));
-        // Let's create an unresolved address
-        InetSocketAddress ad = new InetSocketAddress(HOSTNAME, 1234);
-        try (Socket s = new Socket(sp)) {
-            s.connect(ad, 10000);
-        } catch (UnknownHostException ex) {
-            // OK, that's what we expected
-        } catch (NullPointerException npe) {
-            // Not OK, this used to be the bug
-            throw new RuntimeException("Got a NUllPointerException");
-        } finally {
-            srvr.terminate();
+        try (SocksServer srvr = new SocksServer(0, true)) {
+            srvr.start();
+            Proxy sp = new Proxy(Proxy.Type.SOCKS,
+                    new InetSocketAddress("localhost", srvr.getPort()));
+            // Let's create an unresolved address
+            InetSocketAddress ad = new InetSocketAddress(HOSTNAME, 1234);
+            try (Socket s = new Socket(sp)) {
+                s.connect(ad, 10000);
+            } catch (UnknownHostException ex) {
+                // OK, that's what we expected
+            } catch (NullPointerException npe) {
+                // Not OK, this used to be the bug
+                throw new RuntimeException("Got a NUllPointerException");
+            }
         }
     }
 
--- a/test/jdk/sun/net/www/protocol/http/ProxyTunnelServer.java	Tue Jan 22 15:33:34 2019 +0800
+++ b/test/jdk/sun/net/www/protocol/http/ProxyTunnelServer.java	Thu Jan 24 15:48:05 2019 +0000
@@ -38,33 +38,40 @@
 
 public class ProxyTunnelServer extends Thread {
 
-    private static ServerSocket ss = null;
+    private final ServerSocket ss;
     /*
      * holds the registered user's username and password
      * only one such entry is maintained
      */
-    private String userPlusPass;
+    private volatile String userPlusPass;
 
     // client requesting for a tunnel
-    private Socket clientSocket = null;
+    private volatile Socket clientSocket = null;
 
     /*
      * Origin server's address and port that the client
      * wants to establish the tunnel for communication.
      */
-    private InetAddress serverInetAddr;
-    private int serverPort;
+    private volatile InetAddress serverInetAddr;
+    private volatile int serverPort;
 
     /*
      * denote whether the proxy needs to authorize
      * CONNECT requests.
      */
-    static boolean needAuth = false;
+
+    volatile boolean needAuth = false;
 
     public ProxyTunnelServer() throws IOException {
-        if (ss == null) {
-          ss = (ServerSocket) ServerSocketFactory.getDefault().
-          createServerSocket(0);
+        ss = new ServerSocket(0);
+    }
+
+    static private void close(Closeable c) {
+        try {
+            if (c != null)
+                c.close();
+        } catch (IOException e) {
+            e.printStackTrace();
         }
     }
 
@@ -72,6 +79,11 @@
         needAuth = auth;
     }
 
+    public void terminate() {
+        close(ss);
+        close(clientSocket);
+    }
+
     /*
      * register users with the proxy, by providing username and
      * password. The username and password are used for authorizing the
@@ -81,10 +93,16 @@
         userPlusPass = uname + ":" + passwd;
     }
 
+    volatile boolean makeTunnel;
+
+    public void doTunnel(boolean tunnel) {
+        makeTunnel = tunnel;
+    }
+
     public void run() {
         try {
             clientSocket = ss.accept();
-            processRequests();
+            processRequests(makeTunnel);
         } catch (Exception e) {
             System.out.println("Proxy Failed: " + e);
             e.printStackTrace();
@@ -105,7 +123,7 @@
      * if there is a match, connection is set in tunneling mode. If
      * needAuth is set to false, Proxy-Authorization checks are not made
      */
-    private void processRequests() throws Exception {
+    private void processRequests(boolean makeTunnel) throws Exception {
 
         InputStream in = clientSocket.getInputStream();
         MessageHeader mheader = new MessageHeader(in);
@@ -125,6 +143,13 @@
                    }
                 }
             }
+
+            if (makeTunnel) {
+                retrieveConnectInfo(statusLine);
+                doTunnel();
+                return;
+            }
+
             respondForConnect(needAuth);
 
             // connection set to the tunneling mode
@@ -178,6 +203,9 @@
      * direction.
      */
     private void doTunnel() throws Exception {
+        OutputStream out = clientSocket.getOutputStream();
+        out.write("HTTP/1.1 200 OK\r\n\r\n".getBytes());
+        out.flush();
 
         Socket serverSocket = new Socket(serverInetAddr, serverPort);
         ProxyTunnel clientToServer = new ProxyTunnel(
@@ -202,10 +230,10 @@
      * socket, until both sockets are open and EOF has not been received.
      */
     class ProxyTunnel extends Thread {
-        Socket sockIn;
-        Socket sockOut;
-        InputStream input;
-        OutputStream output;
+        final Socket sockIn;
+        final Socket sockOut;
+        final InputStream input;
+        final OutputStream output;
 
         public ProxyTunnel(Socket sockIn, Socket sockOut)
         throws Exception {
--- a/test/jdk/sun/net/www/protocol/http/TunnelThroughProxy.java	Tue Jan 22 15:33:34 2019 +0800
+++ b/test/jdk/sun/net/www/protocol/http/TunnelThroughProxy.java	Thu Jan 24 15:48:05 2019 +0000
@@ -25,6 +25,7 @@
  * @test
  * @bug 4620362
  * @modules java.base/sun.net.www
+ * @build ProxyTunnelServer
  * @run main/othervm TunnelThroughProxy
  * @summary JSSE not returning proper exception on unknown host
  */
@@ -34,8 +35,13 @@
 
 public class TunnelThroughProxy {
     public static void main(String[] args) throws Exception {
+        nonexistingHostTest();
+        getLocalPortTest();
+    }
+
+    static void nonexistingHostTest() throws Exception {
+        ProxyTunnelServer proxy = setupProxy(false);
         try {
-            setupProxy();
             URL u = new URL("https://www.nonexistent-site.com/");
             URLConnection uc = u.openConnection();
             InputStream is = uc.getInputStream();
@@ -44,16 +50,48 @@
             if (!e.getMessage().matches(".*HTTP\\/.*500.*")) {
                 throw new RuntimeException(e);
             }
+        } finally {
+            proxy.terminate();
         }
     }
-    static void setupProxy() throws IOException {
+
+
+    static void getLocalPortTest() throws Exception {
+        ProxyTunnelServer proxy = setupProxy(true);
+        try {
+            int proxyPort = proxy.getPort();
+            ServerSocket server = new ServerSocket(0);
+            int serverPort = server.getLocalPort();
+
+            Socket sock;
+            sock = new Socket(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", proxyPort)));
+            InetSocketAddress dest = new InetSocketAddress("127.0.0.1", serverPort);
+            sock.connect(dest);
+            int localPort = sock.getLocalPort();
+            if (localPort == proxyPort)
+                throw new RuntimeException("Fail: socket has wrong local port");
+            // check that tunnel really works
+            Socket sock1 = server.accept();
+            OutputStream os = sock1.getOutputStream();
+            os.write(99);
+            os.flush();
+            if (sock.getInputStream().read() != 99)
+                throw new RuntimeException("Tunnel does not work");
+        } finally {
+            proxy.terminate();
+        }
+    }
+
+    static ProxyTunnelServer setupProxy(boolean makeTunnel) throws IOException {
         ProxyTunnelServer pserver = new ProxyTunnelServer();
+        pserver.doTunnel(makeTunnel);
+        int proxyPort = pserver.getPort();
 
         // disable proxy authentication
         pserver.needUserAuth(false);
         pserver.start();
         System.setProperty("https.proxyHost", "localhost");
-        System.setProperty("https.proxyPort", String.valueOf(
-                                        pserver.getPort()));
+        System.setProperty("https.proxyPort", String.valueOf(proxyPort));
+        return pserver;
     }
 }