8220663: Incorrect handling of IPv6 addresses in Socket(Proxy.HTTP)
authorchegar
Tue, 19 Mar 2019 14:27:50 +0000
changeset 54188 791052cc88db
parent 54187 f554d3e88da3
child 54189 dfde3bb48c03
8220663: Incorrect handling of IPv6 addresses in Socket(Proxy.HTTP) Reviewed-by: alanb, michaelm
src/java.base/share/classes/java/net/HttpConnectSocketImpl.java
test/jdk/java/net/Socket/HttpProxy.java
--- a/src/java.base/share/classes/java/net/HttpConnectSocketImpl.java	Mon Mar 18 15:29:19 2019 +0100
+++ b/src/java.base/share/classes/java/net/HttpConnectSocketImpl.java	Tue Mar 19 14:27:50 2019 +0000
@@ -114,14 +114,17 @@
         if (endpoint == null || !(endpoint instanceof InetSocketAddress))
             throw new IllegalArgumentException("Unsupported address type");
         final InetSocketAddress epoint = (InetSocketAddress)endpoint;
-        final String destHost = epoint.isUnresolved() ? epoint.getHostName()
-                                                      : epoint.getAddress().getHostAddress();
+        String destHost = epoint.isUnresolved() ? epoint.getHostName()
+                                                : epoint.getAddress().getHostAddress();
         final int destPort = epoint.getPort();
 
         SecurityManager security = System.getSecurityManager();
         if (security != null)
             security.checkConnect(destHost, destPort);
 
+        if (destHost.contains(":"))
+            destHost = "[" + destHost + "]";
+
         // Connect to the HTTP proxy server
         String urlString = "http://" + destHost + ":" + destPort;
         Socket httpSocket = privilegedDoTunnel(urlString, timeout);
--- a/test/jdk/java/net/Socket/HttpProxy.java	Mon Mar 18 15:29:19 2019 +0100
+++ b/test/jdk/java/net/Socket/HttpProxy.java	Tue Mar 19 14:27:50 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2019, 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
@@ -23,11 +23,12 @@
 
 /*
  * @test
- * @bug 6370908
+ * @bug 6370908 8220663
  * @summary Add support for HTTP_CONNECT proxy in Socket class
  * @modules java.base/sun.net.www
  * @run main HttpProxy
  * @run main/othervm -Djava.net.preferIPv4Stack=true HttpProxy
+ * @run main/othervm -Djava.net.preferIPv6Addresses=true HttpProxy
  */
 
 import java.io.IOException;
@@ -40,6 +41,9 @@
 import java.net.Proxy;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.List;
 import sun.net.www.MessageHeader;
 
 public class HttpProxy {
@@ -50,9 +54,10 @@
     public static void main(String[] args) throws Exception {
         String host;
         int port;
+        ConnectProxyTunnelServer proxy = null;
         if (args.length == 0) {
             // Start internal proxy
-            ConnectProxyTunnelServer proxy = new ConnectProxyTunnelServer();
+            proxy = new ConnectProxyTunnelServer();
             proxy.start();
             host = "localhost";
             port = proxy.getLocalPort();
@@ -66,8 +71,13 @@
             return;
         }
 
-        HttpProxy p = new HttpProxy(host, port);
-        p.test();
+        try {
+            HttpProxy p = new HttpProxy(host, port);
+            p.test();
+        } finally {
+            if (proxy != null)
+                proxy.close();
+        }
     }
 
     public HttpProxy(String proxyHost, int proxyPort) {
@@ -79,26 +89,37 @@
         InetSocketAddress proxyAddress = new InetSocketAddress(proxyHost, proxyPort);
         Proxy httpProxy = new Proxy(Proxy.Type.HTTP, proxyAddress);
 
-        try (ServerSocket ss = new ServerSocket(0);
-             Socket sock = new Socket(httpProxy)) {
-            sock.setSoTimeout(SO_TIMEOUT);
-            sock.setTcpNoDelay(false);
+        try (ServerSocket ss = new ServerSocket(0)) {
+            List<InetSocketAddress> externalAddresses = new ArrayList<>();
+            externalAddresses.add(
+                new InetSocketAddress(InetAddress.getLocalHost(), ss.getLocalPort()));
 
-            InetSocketAddress externalAddress =
-                new InetSocketAddress(InetAddress.getLocalHost(), ss.getLocalPort());
+            if (!"true".equals(System.getProperty("java.net.preferIPv4Stack"))) {
+                byte[] bytes = new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
+                var address = InetAddress.getByAddress(bytes);
+                externalAddresses.add(
+                        new InetSocketAddress(address, ss.getLocalPort()));
+            }
 
-            out.println("Trying to connect to server socket on " + externalAddress);
-            sock.connect(externalAddress);
-            try (Socket externalSock = ss.accept()) {
-                // perform some simple checks
-                check(sock.isBound(), "Socket is not bound");
-                check(sock.isConnected(), "Socket is not connected");
-                check(!sock.isClosed(), "Socket should not be closed");
-                check(sock.getSoTimeout() == SO_TIMEOUT,
-                        "Socket should have a previously set timeout");
-                check(sock.getTcpNoDelay() ==  false, "NODELAY should be false");
+            for (SocketAddress externalAddress : externalAddresses) {
+                try (Socket sock = new Socket(httpProxy)) {
+                    sock.setSoTimeout(SO_TIMEOUT);
+                    sock.setTcpNoDelay(false);
 
-                simpleDataExchange(sock, externalSock);
+                    out.println("Trying to connect to server socket on " + externalAddress);
+                    sock.connect(externalAddress);
+                    try (Socket externalSock = ss.accept()) {
+                        // perform some simple checks
+                        check(sock.isBound(), "Socket is not bound");
+                        check(sock.isConnected(), "Socket is not connected");
+                        check(!sock.isClosed(), "Socket should not be closed");
+                        check(sock.getSoTimeout() == SO_TIMEOUT,
+                                "Socket should have a previously set timeout");
+                        check(sock.getTcpNoDelay() == false, "NODELAY should be false");
+
+                        simpleDataExchange(sock, externalSock);
+                    }
+                }
             }
         }
     }
@@ -108,7 +129,7 @@
     }
 
     static Exception unexpected(Exception e) {
-        out.println("Unexcepted Exception: " + e);
+        out.println("Unexpected Exception: " + e);
         e.printStackTrace();
         return e;
     }
@@ -164,9 +185,10 @@
         return i1 * 256 + i2;
     }
 
-    static class ConnectProxyTunnelServer extends Thread {
+    static class ConnectProxyTunnelServer extends Thread implements AutoCloseable {
 
         private final ServerSocket ss;
+        private volatile boolean closed;
 
         public ConnectProxyTunnelServer() throws IOException {
             ss = new ServerSocket(0);
@@ -174,13 +196,20 @@
 
         @Override
         public void run() {
-            try (Socket clientSocket = ss.accept()) {
-                processRequest(clientSocket);
+            try {
+                while (!closed) {
+                    try (Socket clientSocket = ss.accept()) {
+                        processRequest(clientSocket);
+                    }
+                }
             } catch (Exception e) {
-                out.println("Proxy Failed: " + e);
-                e.printStackTrace();
+                if (!closed) {
+                    out.println("Proxy Failed: " + e);
+                    e.printStackTrace();
+                }
             } finally {
-                try { ss.close(); } catch (IOException x) { unexpected(x); }
+                if (!closed)
+                    try { ss.close(); } catch (IOException x) { unexpected(x); }
             }
         }
 
@@ -191,6 +220,12 @@
             return ss.getLocalPort();
         }
 
+        @Override
+        public void close() throws Exception {
+            closed = true;
+            ss.close();
+        }
+
         /*
          * Processes the CONNECT request
          */
@@ -200,7 +235,7 @@
 
             if (!statusLine.startsWith("CONNECT")) {
                 out.println("proxy server: processes only "
-                                  + "CONNECT method requests, recieved: "
+                                  + "CONNECT method requests, received: "
                                   + statusLine);
                 return;
             }
@@ -246,12 +281,19 @@
                 int endi = connectStr.lastIndexOf(' ');
                 String connectInfo = connectStr.substring(starti+1, endi).trim();
                 // retrieve server name and port
-                endi = connectInfo.indexOf(':');
+                endi = connectInfo.lastIndexOf(':');
                 String name = connectInfo.substring(0, endi);
+
+                if (name.contains(":")) {
+                    if (!(name.startsWith("[") && name.endsWith("]"))) {
+                        throw new IOException("Invalid host:" + name);
+                    }
+                    name = name.substring(1, name.length() - 1);
+                }
                 int port = Integer.parseInt(connectInfo.substring(endi+1));
                 return new InetSocketAddress(name, port);
             } catch (Exception e) {
-                out.println("Proxy recieved a request: " + connectStr);
+                out.println("Proxy received a request: " + connectStr);
                 throw unexpected(e);
             }
         }