Blame SOURCES/8144566-pr3352.patch

e24496
# HG changeset patch
e24496
# User rpatil
e24496
# Date 1474623897 -19800
e24496
#      Fri Sep 23 15:14:57 2016 +0530
e24496
# Node ID fb617df8fbac42e962219e45cbd29b15b5ecdc63
e24496
# Parent  d41592af9af3790fe5eee30ce686d85cff09c942
e24496
8144566, PR3352: Custom HostnameVerifier disables SNI extension
e24496
Reviewed-by: coffeys
e24496
e24496
diff --git a/src/share/classes/sun/security/ssl/SSLSocketImpl.java b/src/share/classes/sun/security/ssl/SSLSocketImpl.java
e24496
--- openjdk/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java
e24496
+++ openjdk/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java
e24496
@@ -1,5 +1,5 @@
e24496
 /*
e24496
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
e24496
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
e24496
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
e24496
  *
e24496
  * This code is free software; you can redistribute it and/or modify it
e24496
@@ -220,6 +220,11 @@
e24496
                                     Collections.<SNIServerName>emptyList();
e24496
     Collection<SNIMatcher>      sniMatchers =
e24496
                                     Collections.<SNIMatcher>emptyList();
e24496
+    // Is the serverNames set to empty with SSLParameters.setServerNames()?
e24496
+    private boolean             noSniExtension = false;
e24496
+
e24496
+    // Is the sniMatchers set to empty with SSLParameters.setSNIMatchers()?
e24496
+    private boolean             noSniMatcher = false;
e24496
 
e24496
     /*
e24496
      * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME *
e24496
@@ -666,6 +671,11 @@
e24496
         }
e24496
 
e24496
         super.connect(endpoint, timeout);
e24496
+
e24496
+        if (host == null || host.length() == 0) {
e24496
+            useImplicitHost(false);
e24496
+        }
e24496
+
e24496
         doneConnect();
e24496
     }
e24496
 
e24496
@@ -2158,41 +2168,61 @@
e24496
         output.r.setVersion(protocolVersion);
e24496
     }
e24496
 
e24496
+    //
e24496
+    // ONLY used by ClientHandshaker for the server hostname during handshaking
e24496
+    //
e24496
     synchronized String getHost() {
e24496
         // Note that the host may be null or empty for localhost.
e24496
         if (host == null || host.length() == 0) {
e24496
-            if (!trustNameService) {
e24496
-                // If the local name service is not trustworthy, reverse host
e24496
-                // name resolution should not be performed for endpoint
e24496
-                // identification.  Use the application original specified
e24496
-                // hostname or IP address instead.
e24496
-                host = getOriginalHostname(getInetAddress());
e24496
-            } else {
e24496
-                host = getInetAddress().getHostName();
e24496
-            }
e24496
+            useImplicitHost(true);
e24496
         }
e24496
 
e24496
         return host;
e24496
     }
e24496
 
e24496
     /*
e24496
-     * Get the original application specified hostname.
e24496
+     * Try to set and use the implicit specified hostname
e24496
      */
e24496
-    private static String getOriginalHostname(InetAddress inetAddress) {
e24496
-        /*
e24496
-         * Get the original hostname via sun.misc.SharedSecrets.
e24496
-         */
e24496
+    private synchronized void useImplicitHost(boolean noSniUpdate) {
e24496
+
e24496
+        // Note: If the local name service is not trustworthy, reverse
e24496
+        // host name resolution should not be performed for endpoint
e24496
+        // identification.  Use the application original specified
e24496
+        // hostname or IP address instead.
e24496
+
e24496
+        // Get the original hostname via jdk.internal.misc.SharedSecrets
e24496
+        InetAddress inetAddress = getInetAddress();
e24496
+        if (inetAddress == null) {      // not connected
e24496
+            return;
e24496
+        }
e24496
+
e24496
         JavaNetAccess jna = SharedSecrets.getJavaNetAccess();
e24496
         String originalHostname = jna.getOriginalHostName(inetAddress);
e24496
+        if ((originalHostname != null) &&
e24496
+                (originalHostname.length() != 0)) {
e24496
 
e24496
-        /*
e24496
-         * If no application specified hostname, use the IP address.
e24496
-         */
e24496
-        if (originalHostname == null || originalHostname.length() == 0) {
e24496
-            originalHostname = inetAddress.getHostAddress();
e24496
+            host = originalHostname;
e24496
+            if (!noSniUpdate && serverNames.isEmpty() && !noSniExtension) {
e24496
+                serverNames =
e24496
+                        Utilities.addToSNIServerNameList(serverNames, host);
e24496
+
e24496
+                if (!roleIsServer &&
e24496
+                        (handshaker != null) && !handshaker.started()) {
e24496
+                    handshaker.setSNIServerNames(serverNames);
e24496
+                }
e24496
+            }
e24496
+
e24496
+            return;
e24496
         }
e24496
 
e24496
-        return originalHostname;
e24496
+        // No explicitly specified hostname, no server name indication.
e24496
+        if (!trustNameService) {
e24496
+            // The local name service is not trustworthy, use IP address.
e24496
+            host = inetAddress.getHostAddress();
e24496
+        } else {
e24496
+            // Use the underlying reverse host name resolution service.
e24496
+            host = getInetAddress().getHostName();
e24496
+        }
e24496
     }
e24496
 
e24496
 
e24496
@@ -2205,6 +2235,10 @@
e24496
         this.host = host;
e24496
         this.serverNames =
e24496
             Utilities.addToSNIServerNameList(this.serverNames, this.host);
e24496
+
e24496
+        if (!roleIsServer && (handshaker != null) && !handshaker.started()) {
e24496
+            handshaker.setSNIServerNames(serverNames);
e24496
+        }
e24496
     }
e24496
 
e24496
     /**
e24496
@@ -2571,8 +2605,21 @@
e24496
         // the super implementation does not handle the following parameters
e24496
         params.setEndpointIdentificationAlgorithm(identificationProtocol);
e24496
         params.setAlgorithmConstraints(algorithmConstraints);
e24496
-        params.setSNIMatchers(sniMatchers);
e24496
-        params.setServerNames(serverNames);
e24496
+
e24496
+        if (sniMatchers.isEmpty() && !noSniMatcher) {
e24496
+            // 'null' indicates none has been set
e24496
+            params.setSNIMatchers(null);
e24496
+        } else {
e24496
+            params.setSNIMatchers(sniMatchers);
e24496
+        }
e24496
+
e24496
+        if (serverNames.isEmpty() && !noSniExtension) {
e24496
+            // 'null' indicates none has been set
e24496
+            params.setServerNames(null);
e24496
+        } else {
e24496
+            params.setServerNames(serverNames);
e24496
+        }
e24496
+
e24496
         params.setUseCipherSuitesOrder(preferLocalCipherSuites);
e24496
 
e24496
         return params;
e24496
@@ -2592,11 +2639,13 @@
e24496
 
e24496
         List<SNIServerName> sniNames = params.getServerNames();
e24496
         if (sniNames != null) {
e24496
+            noSniExtension = sniNames.isEmpty();
e24496
             serverNames = sniNames;
e24496
         }
e24496
 
e24496
         Collection<SNIMatcher> matchers = params.getSNIMatchers();
e24496
         if (matchers != null) {
e24496
+            noSniMatcher = matchers.isEmpty();
e24496
             sniMatchers = matchers;
e24496
         }
e24496
 
e24496
diff --git a/test/javax/net/ssl/ServerName/BestEffortOnLazyConnected.java b/test/javax/net/ssl/ServerName/BestEffortOnLazyConnected.java
e24496
new file mode 100644
e24496
--- /dev/null
e24496
+++ openjdk/jdk/test/javax/net/ssl/ServerName/BestEffortOnLazyConnected.java
e24496
@@ -0,0 +1,337 @@
e24496
+/*
e24496
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
e24496
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
e24496
+ *
e24496
+ * This code is free software; you can redistribute it and/or modify it
e24496
+ * under the terms of the GNU General Public License version 2 only, as
e24496
+ * published by the Free Software Foundation.
e24496
+ *
e24496
+ * This code is distributed in the hope that it will be useful, but WITHOUT
e24496
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
e24496
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
e24496
+ * version 2 for more details (a copy is included in the LICENSE file that
e24496
+ * accompanied this code).
e24496
+ *
e24496
+ * You should have received a copy of the GNU General Public License version
e24496
+ * 2 along with this work; if not, write to the Free Software Foundation,
e24496
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
e24496
+ *
e24496
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
e24496
+ * or visit www.oracle.com if you need additional information or have any
e24496
+ * questions.
e24496
+ */
e24496
+
e24496
+//
e24496
+// SunJSSE does not support dynamic system properties, no way to re-use
e24496
+// system properties in samevm/agentvm mode.
e24496
+//
e24496
+
e24496
+/**
e24496
+ * @test
e24496
+ * @bug 8144566
e24496
+ * @summary Custom HostnameVerifier disables SNI extension
e24496
+ * @run main/othervm BestEffortOnLazyConnected
e24496
+ */
e24496
+
e24496
+import java.io.*;
e24496
+import java.nio.*;
e24496
+import java.nio.channels.*;
e24496
+import java.util.*;
e24496
+import java.net.*;
e24496
+import javax.net.ssl.*;
e24496
+
e24496
+public class BestEffortOnLazyConnected {
e24496
+
e24496
+    /*
e24496
+     * =============================================================
e24496
+     * Set the various variables needed for the tests, then
e24496
+     * specify what tests to run on each side.
e24496
+     */
e24496
+
e24496
+    /*
e24496
+     * Should we run the client or server in a separate thread?
e24496
+     * Both sides can throw exceptions, but do you have a preference
e24496
+     * as to which side should be the main thread.
e24496
+     */
e24496
+    private static final boolean separateServerThread = true;
e24496
+
e24496
+    /*
e24496
+     * Where do we find the keystores?
e24496
+     */
e24496
+    private static final String pathToStores = "../../../../sun/security/ssl/etc";
e24496
+    private static final String keyStoreFile = "keystore";
e24496
+    private static final String trustStoreFile = "truststore";
e24496
+    private static final String passwd = "passphrase";
e24496
+
e24496
+    /*
e24496
+     * Is the server ready to serve?
e24496
+     */
e24496
+    private static volatile boolean serverReady = false;
e24496
+
e24496
+    /*
e24496
+     * Turn on SSL debugging?
e24496
+     */
e24496
+    private static final boolean debug = false;
e24496
+
e24496
+    /*
e24496
+     * the fully qualified domain name of localhost
e24496
+     */
e24496
+    private static String hostname = null;
e24496
+
e24496
+    /*
e24496
+     * If the client or server is doing some kind of object creation
e24496
+     * that the other side depends on, and that thread prematurely
e24496
+     * exits, you may experience a hang.  The test harness will
e24496
+     * terminate all hung threads after its timeout has expired,
e24496
+     * currently 3 minutes by default, but you might try to be
e24496
+     * smart about it....
e24496
+     */
e24496
+
e24496
+    /*
e24496
+     * Define the server side of the test.
e24496
+     *
e24496
+     * If the server prematurely exits, serverReady will be set to true
e24496
+     * to avoid infinite hangs.
e24496
+     */
e24496
+    private void doServerSide() throws Exception {
e24496
+        SSLServerSocketFactory sslssf =
e24496
+            (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
e24496
+        try (SSLServerSocket sslServerSocket =
e24496
+                (SSLServerSocket) sslssf.createServerSocket(serverPort)) {
e24496
+
e24496
+            serverPort = sslServerSocket.getLocalPort();
e24496
+
e24496
+            /*
e24496
+             * Signal Client, we're ready for his connect.
e24496
+             */
e24496
+            serverReady = true;
e24496
+
e24496
+            try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
e24496
+                InputStream sslIS = sslSocket.getInputStream();
e24496
+                OutputStream sslOS = sslSocket.getOutputStream();
e24496
+
e24496
+                sslIS.read();
e24496
+                sslOS.write(85);
e24496
+                sslOS.flush();
e24496
+
e24496
+                ExtendedSSLSession session =
e24496
+                        (ExtendedSSLSession)sslSocket.getSession();
e24496
+                if (session.getRequestedServerNames().isEmpty()) {
e24496
+                    throw new Exception("No expected Server Name Indication");
e24496
+                }
e24496
+            }
e24496
+        }
e24496
+    }
e24496
+
e24496
+    /*
e24496
+     * Define the client side of the test.
e24496
+     *
e24496
+     * If the server prematurely exits, serverReady will be set to true
e24496
+     * to avoid infinite hangs.
e24496
+     */
e24496
+    private void doClientSide() throws Exception {
e24496
+
e24496
+        /*
e24496
+         * Wait for server to get started.
e24496
+         */
e24496
+        while (!serverReady) {
e24496
+            Thread.sleep(50);
e24496
+        }
e24496
+
e24496
+        SSLSocketFactory sslsf =
e24496
+            (SSLSocketFactory) SSLSocketFactory.getDefault();
e24496
+
e24496
+        try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
e24496
+
e24496
+            sslSocket.connect(new InetSocketAddress(hostname, serverPort), 0);
e24496
+
e24496
+            InputStream sslIS = sslSocket.getInputStream();
e24496
+            OutputStream sslOS = sslSocket.getOutputStream();
e24496
+
e24496
+            sslOS.write(280);
e24496
+            sslOS.flush();
e24496
+            sslIS.read();
e24496
+        }
e24496
+    }
e24496
+
e24496
+
e24496
+    /*
e24496
+     * =============================================================
e24496
+     * The remainder is just support stuff
e24496
+     */
e24496
+
e24496
+    // use any free port by default
e24496
+    private volatile int serverPort = 0;
e24496
+
e24496
+    private volatile Exception serverException = null;
e24496
+    private volatile Exception clientException = null;
e24496
+
e24496
+    public static void main(String[] args) throws Exception {
e24496
+        String keyFilename =
e24496
+            System.getProperty("test.src", ".") + "/" + pathToStores +
e24496
+                "/" + keyStoreFile;
e24496
+        String trustFilename =
e24496
+            System.getProperty("test.src", ".") + "/" + pathToStores +
e24496
+                "/" + trustStoreFile;
e24496
+
e24496
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
e24496
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
e24496
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
e24496
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
e24496
+
e24496
+        if (debug) {
e24496
+            System.setProperty("javax.net.debug", "all");
e24496
+        }
e24496
+
e24496
+        try {
e24496
+            hostname = InetAddress.getLocalHost().getCanonicalHostName();
e24496
+        } catch (UnknownHostException uhe) {
e24496
+            System.out.println(
e24496
+                "Ignore the test as the local hostname cannot be determined");
e24496
+
e24496
+            return;
e24496
+        }
e24496
+
e24496
+        System.out.println(
e24496
+                "The fully qualified domain name of the local host is " +
e24496
+                hostname);
e24496
+        // Ignore the test if the hostname does not sound like a domain name.
e24496
+        if ((hostname == null) || hostname.isEmpty() ||
e24496
+                hostname.startsWith("localhost") ||
e24496
+                Character.isDigit(hostname.charAt(hostname.length() - 1))) {
e24496
+
e24496
+            System.out.println("Ignore the test as the local hostname " +
e24496
+                    "cannot be determined as fully qualified domain name");
e24496
+
e24496
+            return;
e24496
+        }
e24496
+
e24496
+        /*
e24496
+         * Start the tests.
e24496
+         */
e24496
+        new BestEffortOnLazyConnected();
e24496
+    }
e24496
+
e24496
+    private Thread clientThread = null;
e24496
+    private Thread serverThread = null;
e24496
+
e24496
+    /*
e24496
+     * Primary constructor, used to drive remainder of the test.
e24496
+     *
e24496
+     * Fork off the other side, then do your work.
e24496
+     */
e24496
+    BestEffortOnLazyConnected() throws Exception {
e24496
+        try {
e24496
+            if (separateServerThread) {
e24496
+                startServer(true);
e24496
+                startClient(false);
e24496
+            } else {
e24496
+                startClient(true);
e24496
+                startServer(false);
e24496
+            }
e24496
+        } catch (Exception e) {
e24496
+            // swallow for now.  Show later
e24496
+        }
e24496
+
e24496
+        /*
e24496
+         * Wait for other side to close down.
e24496
+         */
e24496
+        if (separateServerThread) {
e24496
+            serverThread.join();
e24496
+        } else {
e24496
+            clientThread.join();
e24496
+        }
e24496
+
e24496
+        /*
e24496
+         * When we get here, the test is pretty much over.
e24496
+         * Which side threw the error?
e24496
+         */
e24496
+        Exception local;
e24496
+        Exception remote;
e24496
+        String whichRemote;
e24496
+
e24496
+        if (separateServerThread) {
e24496
+            remote = serverException;
e24496
+            local = clientException;
e24496
+            whichRemote = "server";
e24496
+        } else {
e24496
+            remote = clientException;
e24496
+            local = serverException;
e24496
+            whichRemote = "client";
e24496
+        }
e24496
+
e24496
+        /*
e24496
+         * If both failed, return the curthread's exception, but also
e24496
+         * print the remote side Exception
e24496
+         */
e24496
+        if ((local != null) && (remote != null)) {
e24496
+            System.out.println(whichRemote + " also threw:");
e24496
+            remote.printStackTrace();
e24496
+            System.out.println();
e24496
+            throw local;
e24496
+        }
e24496
+
e24496
+        if (remote != null) {
e24496
+            throw remote;
e24496
+        }
e24496
+
e24496
+        if (local != null) {
e24496
+            throw local;
e24496
+        }
e24496
+    }
e24496
+
e24496
+    private void startServer(boolean newThread) throws Exception {
e24496
+        if (newThread) {
e24496
+            serverThread = new Thread() {
e24496
+                public void run() {
e24496
+                    try {
e24496
+                        doServerSide();
e24496
+                    } catch (Exception e) {
e24496
+                        /*
e24496
+                         * Our server thread just died.
e24496
+                         *
e24496
+                         * Release the client, if not active already...
e24496
+                         */
e24496
+                        System.err.println("Server died...");
e24496
+                        serverReady = true;
e24496
+                        serverException = e;
e24496
+                    }
e24496
+                }
e24496
+            };
e24496
+            serverThread.start();
e24496
+        } else {
e24496
+            try {
e24496
+                doServerSide();
e24496
+            } catch (Exception e) {
e24496
+                serverException = e;
e24496
+            } finally {
e24496
+                serverReady = true;
e24496
+            }
e24496
+        }
e24496
+    }
e24496
+
e24496
+    private void startClient(boolean newThread) throws Exception {
e24496
+        if (newThread) {
e24496
+            clientThread = new Thread() {
e24496
+                public void run() {
e24496
+                    try {
e24496
+                        doClientSide();
e24496
+                    } catch (Exception e) {
e24496
+                        /*
e24496
+                         * Our client thread just died.
e24496
+                         */
e24496
+                        System.err.println("Client died...");
e24496
+                        clientException = e;
e24496
+                    }
e24496
+                }
e24496
+            };
e24496
+            clientThread.start();
e24496
+        } else {
e24496
+            try {
e24496
+                doClientSide();
e24496
+            } catch (Exception e) {
e24496
+                clientException = e;
e24496
+            }
e24496
+        }
e24496
+    }
e24496
+}
e24496
diff --git a/test/sun/net/www/protocol/https/HttpsURLConnection/ImpactOnSNI.java b/test/sun/net/www/protocol/https/HttpsURLConnection/ImpactOnSNI.java
e24496
new file mode 100644
e24496
--- /dev/null
e24496
+++ openjdk/jdk/test/sun/net/www/protocol/https/HttpsURLConnection/ImpactOnSNI.java
e24496
@@ -0,0 +1,390 @@
e24496
+/*
e24496
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
e24496
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
e24496
+ *
e24496
+ * This code is free software; you can redistribute it and/or modify it
e24496
+ * under the terms of the GNU General Public License version 2 only, as
e24496
+ * published by the Free Software Foundation.
e24496
+ *
e24496
+ * This code is distributed in the hope that it will be useful, but WITHOUT
e24496
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
e24496
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
e24496
+ * version 2 for more details (a copy is included in the LICENSE file that
e24496
+ * accompanied this code).
e24496
+ *
e24496
+ * You should have received a copy of the GNU General Public License version
e24496
+ * 2 along with this work; if not, write to the Free Software Foundation,
e24496
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
e24496
+ *
e24496
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
e24496
+ * or visit www.oracle.com if you need additional information or have any
e24496
+ * questions.
e24496
+ */
e24496
+
e24496
+//
e24496
+// SunJSSE does not support dynamic system properties, no way to re-use
e24496
+// system properties in samevm/agentvm mode.
e24496
+//
e24496
+
e24496
+/*
e24496
+ * @test
e24496
+ * @bug 8144566
e24496
+ * @summary Custom HostnameVerifier disables SNI extension
e24496
+ * @run main/othervm ImpactOnSNI
e24496
+ */
e24496
+
e24496
+import java.io.*;
e24496
+import java.net.*;
e24496
+import javax.net.ssl.*;
e24496
+
e24496
+public class ImpactOnSNI {
e24496
+
e24496
+    /*
e24496
+     * =============================================================
e24496
+     * Set the various variables needed for the tests, then
e24496
+     * specify what tests to run on each side.
e24496
+     */
e24496
+
e24496
+    /*
e24496
+     * Should we run the client or server in a separate thread?
e24496
+     * Both sides can throw exceptions, but do you have a preference
e24496
+     * as to which side should be the main thread.
e24496
+     */
e24496
+    private static final boolean separateServerThread = true;
e24496
+
e24496
+    /*
e24496
+     * Where do we find the keystores?
e24496
+     */
e24496
+    private static final String pathToStores =
e24496
+                                        "../../../../../../sun/security/ssl/etc";
e24496
+    private static final String keyStoreFile = "keystore";
e24496
+    private static final String trustStoreFile = "truststore";
e24496
+    private static final String passwd = "passphrase";
e24496
+
e24496
+    /*
e24496
+     * Is the server ready to serve?
e24496
+     */
e24496
+    private static volatile boolean serverReady = false;
e24496
+
e24496
+    /*
e24496
+     * Is the connection ready to close?
e24496
+     */
e24496
+    private static volatile boolean closeReady = false;
e24496
+
e24496
+    /*
e24496
+     * Turn on SSL debugging?
e24496
+     */
e24496
+    private static final boolean debug = false;
e24496
+
e24496
+    /*
e24496
+     * Message posted
e24496
+     */
e24496
+    private static final String postMsg = "HTTP post on a https server";
e24496
+
e24496
+    /*
e24496
+     * the fully qualified domain name of localhost
e24496
+     */
e24496
+    private static String hostname = null;
e24496
+
e24496
+    /*
e24496
+     * If the client or server is doing some kind of object creation
e24496
+     * that the other side depends on, and that thread prematurely
e24496
+     * exits, you may experience a hang.  The test harness will
e24496
+     * terminate all hung threads after its timeout has expired,
e24496
+     * currently 3 minutes by default, but you might try to be
e24496
+     * smart about it....
e24496
+     */
e24496
+
e24496
+    /*
e24496
+     * Define the server side of the test.
e24496
+     *
e24496
+     * If the server prematurely exits, serverReady will be set to true
e24496
+     * to avoid infinite hangs.
e24496
+     */
e24496
+    private void doServerSide() throws Exception {
e24496
+        SSLServerSocketFactory sslssf =
e24496
+            (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
e24496
+        try (SSLServerSocket sslServerSocket =
e24496
+                (SSLServerSocket)sslssf.createServerSocket(serverPort)) {
e24496
+
e24496
+            serverPort = sslServerSocket.getLocalPort();
e24496
+
e24496
+            /*
e24496
+             * Signal Client, we're ready for his connect.
e24496
+             */
e24496
+            serverReady = true;
e24496
+
e24496
+            /*
e24496
+             * Accept connections
e24496
+             */
e24496
+            try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
e24496
+                InputStream sslIS = sslSocket.getInputStream();
e24496
+                OutputStream sslOS = sslSocket.getOutputStream();
e24496
+                BufferedReader br =
e24496
+                        new BufferedReader(new InputStreamReader(sslIS));
e24496
+                PrintStream ps = new PrintStream(sslOS);
e24496
+
e24496
+                // process HTTP POST request from client
e24496
+                System.out.println("status line: " + br.readLine());
e24496
+                String msg = null;
e24496
+                while ((msg = br.readLine()) != null && msg.length() > 0);
e24496
+
e24496
+                msg = br.readLine();
e24496
+                if (msg.equals(postMsg)) {
e24496
+                    ps.println("HTTP/1.1 200 OK\n\n");
e24496
+                } else {
e24496
+                    ps.println("HTTP/1.1 500 Not OK\n\n");
e24496
+                }
e24496
+                ps.flush();
e24496
+
e24496
+                ExtendedSSLSession session =
e24496
+                        (ExtendedSSLSession)sslSocket.getSession();
e24496
+                if (session.getRequestedServerNames().isEmpty()) {
e24496
+                    throw new Exception("No expected Server Name Indication");
e24496
+                }
e24496
+
e24496
+                // close the socket
e24496
+                while (!closeReady) {
e24496
+                    Thread.sleep(50);
e24496
+                }
e24496
+            }
e24496
+        }
e24496
+    }
e24496
+
e24496
+    /*
e24496
+     * Define the client side of the test.
e24496
+     *
e24496
+     * If the server prematurely exits, serverReady will be set to true
e24496
+     * to avoid infinite hangs.
e24496
+     */
e24496
+    private void doClientSide() throws Exception {
e24496
+        /*
e24496
+         * Wait for server to get started.
e24496
+         */
e24496
+        while (!serverReady) {
e24496
+            Thread.sleep(50);
e24496
+        }
e24496
+
e24496
+        // Send HTTP POST request to server
e24496
+        URL url = new URL("https://" + hostname + ":" + serverPort);
e24496
+
e24496
+        HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier());
e24496
+        HttpsURLConnection http = (HttpsURLConnection)url.openConnection();
e24496
+        http.setDoOutput(true);
e24496
+
e24496
+        http.setRequestMethod("POST");
e24496
+        PrintStream ps = new PrintStream(http.getOutputStream());
e24496
+        try {
e24496
+            ps.println(postMsg);
e24496
+            ps.flush();
e24496
+            if (http.getResponseCode() != 200) {
e24496
+                throw new RuntimeException("test Failed");
e24496
+            }
e24496
+        } finally {
e24496
+            ps.close();
e24496
+            http.disconnect();
e24496
+            closeReady = true;
e24496
+        }
e24496
+    }
e24496
+
e24496
+    private static class NameVerifier implements HostnameVerifier {
e24496
+        public boolean verify(String hostname, SSLSession session) {
e24496
+            return true;
e24496
+        }
e24496
+    }
e24496
+
e24496
+    /*
e24496
+     * =============================================================
e24496
+     * The remainder is just support stuff
e24496
+     */
e24496
+
e24496
+    // use any free port by default
e24496
+    private volatile int serverPort = 0;
e24496
+
e24496
+    private volatile Exception serverException = null;
e24496
+    private volatile Exception clientException = null;
e24496
+
e24496
+    public static void main(String[] args) throws Exception {
e24496
+        String keyFilename =
e24496
+            System.getProperty("test.src", "./") + "/" + pathToStores +
e24496
+                "/" + keyStoreFile;
e24496
+        String trustFilename =
e24496
+            System.getProperty("test.src", "./") + "/" + pathToStores +
e24496
+                "/" + trustStoreFile;
e24496
+
e24496
+        System.setProperty("javax.net.ssl.keyStore", keyFilename);
e24496
+        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
e24496
+        System.setProperty("javax.net.ssl.trustStore", trustFilename);
e24496
+        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
e24496
+
e24496
+        if (debug) {
e24496
+            System.setProperty("javax.net.debug", "all");
e24496
+        }
e24496
+
e24496
+        try {
e24496
+            hostname = InetAddress.getLocalHost().getCanonicalHostName();
e24496
+        } catch (UnknownHostException uhe) {
e24496
+            System.out.println(
e24496
+                "Ignore the test as the local hostname cannot be determined");
e24496
+
e24496
+            return;
e24496
+        }
e24496
+
e24496
+        System.out.println(
e24496
+                "The fully qualified domain name of the local host is " +
e24496
+                hostname);
e24496
+        // Ignore the test if the hostname does not sound like a domain name.
e24496
+        if ((hostname == null) || hostname.isEmpty() ||
e24496
+                hostname.startsWith("localhost") ||
e24496
+                Character.isDigit(hostname.charAt(hostname.length() - 1))) {
e24496
+
e24496
+            System.out.println("Ignore the test as the local hostname " +
e24496
+                    "cannot be determined as fully qualified domain name");
e24496
+
e24496
+            return;
e24496
+        }
e24496
+
e24496
+        /*
e24496
+         * Start the tests.
e24496
+         */
e24496
+        new ImpactOnSNI();
e24496
+    }
e24496
+
e24496
+    private Thread clientThread = null;
e24496
+    private Thread serverThread = null;
e24496
+
e24496
+    /*
e24496
+     * Primary constructor, used to drive remainder of the test.
e24496
+     *
e24496
+     * Fork off the other side, then do your work.
e24496
+     */
e24496
+    ImpactOnSNI() throws Exception {
e24496
+        Exception startException = null;
e24496
+        try {
e24496
+            if (separateServerThread) {
e24496
+                startServer(true);
e24496
+                startClient(false);
e24496
+            } else {
e24496
+                startClient(true);
e24496
+                startServer(false);
e24496
+            }
e24496
+        } catch (Exception e) {
e24496
+            startException = e;
e24496
+        }
e24496
+
e24496
+        /*
e24496
+         * Wait for other side to close down.
e24496
+         */
e24496
+        if (separateServerThread) {
e24496
+            if (serverThread != null) {
e24496
+                serverThread.join();
e24496
+            }
e24496
+        } else {
e24496
+            if (clientThread != null) {
e24496
+                clientThread.join();
e24496
+            }
e24496
+        }
e24496
+
e24496
+        /*
e24496
+         * When we get here, the test is pretty much over.
e24496
+         * Which side threw the error?
e24496
+         */
e24496
+        Exception local;
e24496
+        Exception remote;
e24496
+
e24496
+        if (separateServerThread) {
e24496
+            remote = serverException;
e24496
+            local = clientException;
e24496
+        } else {
e24496
+            remote = clientException;
e24496
+            local = serverException;
e24496
+        }
e24496
+
e24496
+        Exception exception = null;
e24496
+
e24496
+        /*
e24496
+         * Check various exception conditions.
e24496
+         */
e24496
+        if ((local != null) && (remote != null)) {
e24496
+            // If both failed, return the curthread's exception.
e24496
+            local.initCause(remote);
e24496
+            exception = local;
e24496
+        } else if (local != null) {
e24496
+            exception = local;
e24496
+        } else if (remote != null) {
e24496
+            exception = remote;
e24496
+        } else if (startException != null) {
e24496
+            exception = startException;
e24496
+        }
e24496
+
e24496
+        /*
e24496
+         * If there was an exception *AND* a startException,
e24496
+         * output it.
e24496
+         */
e24496
+        if (exception != null) {
e24496
+            if (exception != startException && startException != null) {
e24496
+                exception.addSuppressed(startException);
e24496
+            }
e24496
+            throw exception;
e24496
+        }
e24496
+
e24496
+        // Fall-through: no exception to throw!
e24496
+    }
e24496
+
e24496
+    private void startServer(boolean newThread) throws Exception {
e24496
+        if (newThread) {
e24496
+            serverThread = new Thread() {
e24496
+                @Override
e24496
+                public void run() {
e24496
+                    try {
e24496
+                        doServerSide();
e24496
+                    } catch (Exception e) {
e24496
+                        /*
e24496
+                         * Our server thread just died.
e24496
+                         *
e24496
+                         * Release the client, if not active already...
e24496
+                         */
e24496
+                        System.err.println("Server died...");
e24496
+                        serverReady = true;
e24496
+                        serverException = e;
e24496
+                    }
e24496
+                }
e24496
+            };
e24496
+            serverThread.start();
e24496
+        } else {
e24496
+            try {
e24496
+                doServerSide();
e24496
+            } catch (Exception e) {
e24496
+                serverException = e;
e24496
+            } finally {
e24496
+                serverReady = true;
e24496
+            }
e24496
+        }
e24496
+    }
e24496
+
e24496
+    private void startClient(boolean newThread) throws Exception {
e24496
+        if (newThread) {
e24496
+            clientThread = new Thread() {
e24496
+                @Override
e24496
+                public void run() {
e24496
+                    try {
e24496
+                        doClientSide();
e24496
+                    } catch (Exception e) {
e24496
+                        /*
e24496
+                         * Our client thread just died.
e24496
+                         */
e24496
+                        System.err.println("Client died...");
e24496
+                        clientException = e;
e24496
+                    }
e24496
+                }
e24496
+            };
e24496
+            clientThread.start();
e24496
+        } else {
e24496
+            try {
e24496
+                doClientSide();
e24496
+            } catch (Exception e) {
e24496
+                clientException = e;
e24496
+            }
e24496
+        }
e24496
+    }
e24496
+}