1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 //
  25 // SunJSSE does not support dynamic system properties, no way to re-use
  26 // system properties in samevm/agentvm mode.
  27 //
  28 
  29 /**
  30  * @test
  31  * @bug 8144566
  32  * @summary Custom HostnameVerifier disables SNI extension
  33  * @run main/othervm BestEffortOnLazyConnected
  34  */
  35 
  36 import java.io.*;
  37 import java.nio.*;
  38 import java.nio.channels.*;
  39 import java.util.*;
  40 import java.net.*;
  41 import javax.net.ssl.*;
  42 
  43 public class BestEffortOnLazyConnected {
  44 
  45     /*
  46      * =============================================================
  47      * Set the various variables needed for the tests, then
  48      * specify what tests to run on each side.
  49      */
  50 
  51     /*
  52      * Should we run the client or server in a separate thread?
  53      * Both sides can throw exceptions, but do you have a preference
  54      * as to which side should be the main thread.
  55      */
  56     private static final boolean separateServerThread = true;
  57 
  58     /*
  59      * Where do we find the keystores?
  60      */
  61     private static final String pathToStores = "../etc";
  62     private static final String keyStoreFile = "keystore";
  63     private static final String trustStoreFile = "truststore";
  64     private static final String passwd = "passphrase";
  65 
  66     /*
  67      * Is the server ready to serve?
  68      */
  69     private static volatile boolean serverReady = false;
  70 
  71     /*
  72      * Turn on SSL debugging?
  73      */
  74     private static final boolean debug = false;
  75 
  76     /*
  77      * the fully qualified domain name of localhost
  78      */
  79     private static String hostname = null;
  80 
  81     /*
  82      * If the client or server is doing some kind of object creation
  83      * that the other side depends on, and that thread prematurely
  84      * exits, you may experience a hang.  The test harness will
  85      * terminate all hung threads after its timeout has expired,
  86      * currently 3 minutes by default, but you might try to be
  87      * smart about it....
  88      */
  89 
  90     /*
  91      * Define the server side of the test.
  92      *
  93      * If the server prematurely exits, serverReady will be set to true
  94      * to avoid infinite hangs.
  95      */
  96     private void doServerSide() throws Exception {
  97         SSLServerSocketFactory sslssf =
  98             (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
  99         try (SSLServerSocket sslServerSocket =
 100                 (SSLServerSocket) sslssf.createServerSocket(serverPort)) {
 101 
 102             serverPort = sslServerSocket.getLocalPort();
 103 
 104             /*
 105              * Signal Client, we're ready for his connect.
 106              */
 107             serverReady = true;
 108 
 109             try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
 110                 InputStream sslIS = sslSocket.getInputStream();
 111                 OutputStream sslOS = sslSocket.getOutputStream();
 112 
 113                 sslIS.read();
 114                 sslOS.write(85);
 115                 sslOS.flush();
 116 
 117                 ExtendedSSLSession session =
 118                         (ExtendedSSLSession)sslSocket.getSession();
 119                 if (session.getRequestedServerNames().isEmpty()) {
 120                     throw new Exception("No expected Server Name Indication");
 121                 }
 122             }
 123         }
 124     }
 125 
 126     /*
 127      * Define the client side of the test.
 128      *
 129      * If the server prematurely exits, serverReady will be set to true
 130      * to avoid infinite hangs.
 131      */
 132     private void doClientSide() throws Exception {
 133 
 134         /*
 135          * Wait for server to get started.
 136          */
 137         while (!serverReady) {
 138             Thread.sleep(50);
 139         }
 140 
 141         SSLSocketFactory sslsf =
 142             (SSLSocketFactory) SSLSocketFactory.getDefault();
 143 
 144         try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
 145 
 146             sslSocket.connect(new InetSocketAddress(hostname, serverPort), 0);
 147 
 148             InputStream sslIS = sslSocket.getInputStream();
 149             OutputStream sslOS = sslSocket.getOutputStream();
 150 
 151             sslOS.write(280);
 152             sslOS.flush();
 153             sslIS.read();
 154         }
 155     }
 156 
 157 
 158     /*
 159      * =============================================================
 160      * The remainder is just support stuff
 161      */
 162 
 163     // use any free port by default
 164     private volatile int serverPort = 0;
 165 
 166     private volatile Exception serverException = null;
 167     private volatile Exception clientException = null;
 168 
 169     public static void main(String[] args) throws Exception {
 170         String keyFilename =
 171             System.getProperty("test.src", ".") + "/" + pathToStores +
 172                 "/" + keyStoreFile;
 173         String trustFilename =
 174             System.getProperty("test.src", ".") + "/" + pathToStores +
 175                 "/" + trustStoreFile;
 176 
 177         System.setProperty("javax.net.ssl.keyStore", keyFilename);
 178         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
 179         System.setProperty("javax.net.ssl.trustStore", trustFilename);
 180         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
 181 
 182         if (debug) {
 183             System.setProperty("javax.net.debug", "all");
 184         }
 185 
 186         try {
 187             hostname = InetAddress.getLocalHost().getCanonicalHostName();
 188         } catch (UnknownHostException uhe) {
 189             System.out.println(
 190                 "Ignore the test as the local hostname cannot be determined");
 191 
 192             return;
 193         }
 194 
 195         System.out.println(
 196                 "The fully qualified domain name of the local host is " +
 197                 hostname);
 198         // Ignore the test if the hostname does not sound like a domain name.
 199         if ((hostname == null) || hostname.isEmpty() ||
 200                 hostname.startsWith("localhost") ||
 201                 Character.isDigit(hostname.charAt(hostname.length() - 1))) {
 202 
 203             System.out.println("Ignore the test as the local hostname " +
 204                     "cannot be determined as fully qualified domain name");
 205 
 206             return;
 207         }
 208 
 209         /*
 210          * Start the tests.
 211          */
 212         new BestEffortOnLazyConnected();
 213     }
 214 
 215     private Thread clientThread = null;
 216     private Thread serverThread = null;
 217 
 218     /*
 219      * Primary constructor, used to drive remainder of the test.
 220      *
 221      * Fork off the other side, then do your work.
 222      */
 223     BestEffortOnLazyConnected() throws Exception {
 224         try {
 225             if (separateServerThread) {
 226                 startServer(true);
 227                 startClient(false);
 228             } else {
 229                 startClient(true);
 230                 startServer(false);
 231             }
 232         } catch (Exception e) {
 233             // swallow for now.  Show later
 234         }
 235 
 236         /*
 237          * Wait for other side to close down.
 238          */
 239         if (separateServerThread) {
 240             serverThread.join();
 241         } else {
 242             clientThread.join();
 243         }
 244 
 245         /*
 246          * When we get here, the test is pretty much over.
 247          * Which side threw the error?
 248          */
 249         Exception local;
 250         Exception remote;
 251         String whichRemote;
 252 
 253         if (separateServerThread) {
 254             remote = serverException;
 255             local = clientException;
 256             whichRemote = "server";
 257         } else {
 258             remote = clientException;
 259             local = serverException;
 260             whichRemote = "client";
 261         }
 262 
 263         /*
 264          * If both failed, return the curthread's exception, but also
 265          * print the remote side Exception
 266          */
 267         if ((local != null) && (remote != null)) {
 268             System.out.println(whichRemote + " also threw:");
 269             remote.printStackTrace();
 270             System.out.println();
 271             throw local;
 272         }
 273 
 274         if (remote != null) {
 275             throw remote;
 276         }
 277 
 278         if (local != null) {
 279             throw local;
 280         }
 281     }
 282 
 283     private void startServer(boolean newThread) throws Exception {
 284         if (newThread) {
 285             serverThread = new Thread() {
 286                 public void run() {
 287                     try {
 288                         doServerSide();
 289                     } catch (Exception e) {
 290                         /*
 291                          * Our server thread just died.
 292                          *
 293                          * Release the client, if not active already...
 294                          */
 295                         System.err.println("Server died...");
 296                         serverReady = true;
 297                         serverException = e;
 298                     }
 299                 }
 300             };
 301             serverThread.start();
 302         } else {
 303             try {
 304                 doServerSide();
 305             } catch (Exception e) {
 306                 serverException = e;
 307             } finally {
 308                 serverReady = true;
 309             }
 310         }
 311     }
 312 
 313     private void startClient(boolean newThread) throws Exception {
 314         if (newThread) {
 315             clientThread = new Thread() {
 316                 public void run() {
 317                     try {
 318                         doClientSide();
 319                     } catch (Exception e) {
 320                         /*
 321                          * Our client thread just died.
 322                          */
 323                         System.err.println("Client died...");
 324                         clientException = e;
 325                     }
 326                 }
 327             };
 328             clientThread.start();
 329         } else {
 330             try {
 331                 doClientSide();
 332             } catch (Exception e) {
 333                 clientException = e;
 334             }
 335         }
 336     }
 337 }