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 // Please run in othervm mode.  SunJSSE does not support dynamic system
  25 // properties, no way to re-use system properties in samevm/agentvm mode.
  26 
  27 /*
  28  * @test
  29  * @bug 8161106
  30  * @summary Improve SSLSocket test template
  31  * @run main/othervm SSLSocketSample
  32  */
  33 
  34 import java.io.*;
  35 import javax.net.ssl.*;
  36 import java.net.*;
  37 import java.util.concurrent.CountDownLatch;
  38 import java.util.concurrent.TimeUnit;
  39 
  40 /**
  41  * Template to help speed your client/server tests.
  42  */
  43 public final class SSLSocketSample {
  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 = false;
  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      * Turn on SSL debugging?
  68      */
  69     private static final boolean debug = false;
  70 
  71     /*
  72      * Is the server ready to serve?
  73      */
  74     private static final CountDownLatch serverCondition = new CountDownLatch(1);
  75 
  76     /*
  77      * Is the client ready to handshake?
  78      */
  79     private static final CountDownLatch clientCondition = new CountDownLatch(1);
  80 
  81     /*
  82      * What's the server port?  Use any free port by default
  83      */
  84     private volatile int serverPort = 0;
  85 
  86     /*
  87      * If the client or server is doing some kind of object creation
  88      * that the other side depends on, and that thread prematurely
  89      * exits, you may experience a hang.  The test harness will
  90      * terminate all hung threads after its timeout has expired,
  91      * currently 3 minutes by default, but you might try to be
  92      * smart about it....
  93      */
  94 
  95     /*
  96      * Define the server side of the test.
  97      */
  98     void doServerSide() throws Exception {
  99         SSLServerSocket sslServerSocket;
 100 
 101         // kick start the server side service
 102         SSLServerSocketFactory sslssf =
 103                 (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
 104         sslServerSocket =
 105                 (SSLServerSocket)sslssf.createServerSocket(serverPort);
 106 
 107         serverPort = sslServerSocket.getLocalPort();
 108 
 109         // Signal the client, the server is ready to accept connection.
 110         serverCondition.countDown();
 111 
 112 
 113         // Try to accept a connection in 30 seconds.
 114         SSLSocket sslSocket;
 115         try {
 116             sslServerSocket.setSoTimeout(30000);
 117             sslSocket = (SSLSocket)sslServerSocket.accept();
 118         } catch (SocketTimeoutException ste) {
 119             sslServerSocket.close();
 120 
 121             // Ignore the test case if no connection within 30 seconds.
 122             System.out.println(
 123                 "No incoming client connection in 30 seconds. " +
 124                 "Ignore in server side.");
 125             return;
 126         }
 127 
 128         // handle the connection
 129         try {
 130             // Is it the expected client connection?
 131             //
 132             // Naughty test cases or third party routines may try to
 133             // connection to this server port unintentionally.  In
 134             // order to mitigate the impact of unexpected client
 135             // connections and avoid intermittent failure, it should
 136             // be checked that the accepted connection is really linked
 137             // to the expected client.
 138             boolean clientIsReady =
 139                     clientCondition.await(30L, TimeUnit.SECONDS);
 140 
 141             if (clientIsReady) {
 142                 // Run the application in server side.
 143                 runServerApplication(sslSocket);
 144             } else {    // Otherwise, ignore
 145                 // We don't actually care about plain socket connections
 146                 // for TLS communication testing generally.  Just ignore
 147                 // the test if the accepted connection is not linked to
 148                 // the expected client or the client connection timeout
 149                 // in 30 seconds.
 150                 System.out.println(
 151                         "The client is not the expected one or timeout. " +
 152                         "Ignore in server side.");
 153             }
 154         } finally {
 155             sslSocket.close();
 156             sslServerSocket.close();
 157         }
 158     }
 159 
 160     /*
 161      * Define the server side application of the test for the specified socket.
 162      */
 163     void runServerApplication(SSLSocket socket) throws Exception {
 164         // here comes the test logic
 165         InputStream sslIS = socket.getInputStream();
 166         OutputStream sslOS = socket.getOutputStream();
 167 
 168         sslIS.read();
 169         sslOS.write(85);
 170         sslOS.flush();
 171     }
 172 
 173     /*
 174      * Define the client side of the test.
 175      */
 176     void doClientSide() throws Exception {
 177 
 178         // Wait for server to get started.
 179         //
 180         // The server side takes care of the issue if the server cannot
 181         // get started in 90 seconds.  The client side would just ignore
 182         // the test case if the serer is not ready.
 183         boolean serverIsReady =
 184                 serverCondition.await(90L, TimeUnit.SECONDS);
 185         if (!serverIsReady) {
 186             System.out.println(
 187                     "The server is not ready yet in 90 seconds. " +
 188                     "Ignore in client side.");
 189             return;
 190         }
 191 
 192         SSLSocketFactory sslsf =
 193             (SSLSocketFactory)SSLSocketFactory.getDefault();
 194         try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
 195             try {
 196                 sslSocket.connect(
 197                         new InetSocketAddress("localhost", serverPort), 15000);
 198             } catch (IOException ioe) {
 199                 // The server side may be impacted by naughty test cases or
 200                 // third party routines, and cannot accept connections.
 201                 //
 202                 // Just ignore the test if the connection cannot be
 203                 // established.
 204                 System.out.println(
 205                         "Cannot make a connection in 15 seconds. " +
 206                         "Ignore in client side.");
 207                 return;
 208             }
 209 
 210             // OK, here the client and server get connected.
 211 
 212             // Signal the server, the client is ready to communicate.
 213             clientCondition.countDown();
 214 
 215             // There is still a chance in theory that the server thread may
 216             // wait client-ready timeout and then quit.  The chance should
 217             // be really rare so we don't consider it until it becomes a
 218             // real problem.
 219 
 220             // Run the application in client side.
 221             runClientApplication(sslSocket);
 222         }
 223     }
 224 
 225     /*
 226      * Define the server side application of the test for the specified socket.
 227      */
 228     void runClientApplication(SSLSocket socket) throws Exception {
 229         InputStream sslIS = socket.getInputStream();
 230         OutputStream sslOS = socket.getOutputStream();
 231 
 232         sslOS.write(280);
 233         sslOS.flush();
 234         sslIS.read();
 235     }
 236 
 237     /*
 238      * =============================================================
 239      * The remainder is just support stuff
 240      */
 241 
 242     private volatile Exception serverException = null;
 243     private volatile Exception clientException = null;
 244 
 245     public static void main(String[] args) throws Exception {
 246         String keyFilename =
 247             System.getProperty("test.src", ".") + "/" + pathToStores +
 248                 "/" + keyStoreFile;
 249         String trustFilename =
 250             System.getProperty("test.src", ".") + "/" + pathToStores +
 251                 "/" + trustStoreFile;
 252 
 253         System.setProperty("javax.net.ssl.keyStore", keyFilename);
 254         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
 255         System.setProperty("javax.net.ssl.trustStore", trustFilename);
 256         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
 257 
 258         if (debug) {
 259             System.setProperty("javax.net.debug", "all");
 260         }
 261 
 262         /*
 263          * Start the tests.
 264          */
 265         new SSLSocketSample();
 266     }
 267 
 268     private Thread clientThread = null;
 269     private Thread serverThread = null;
 270 
 271     /*
 272      * Primary constructor, used to drive remainder of the test.
 273      *
 274      * Fork off the other side, then do your work.
 275      */
 276     SSLSocketSample() throws Exception {
 277         Exception startException = null;
 278         try {
 279             if (separateServerThread) {
 280                 startServer(true);
 281                 startClient(false);
 282             } else {
 283                 startClient(true);
 284                 startServer(false);
 285             }
 286         } catch (Exception e) {
 287             startException = e;
 288         }
 289 
 290         /*
 291          * Wait for other side to close down.
 292          */
 293         if (separateServerThread) {
 294             if (serverThread != null) {
 295                 serverThread.join();
 296             }
 297         } else {
 298             if (clientThread != null) {
 299                 clientThread.join();
 300             }
 301         }
 302 
 303         /*
 304          * When we get here, the test is pretty much over.
 305          * Which side threw the error?
 306          */
 307         Exception local;
 308         Exception remote;
 309 
 310         if (separateServerThread) {
 311             remote = serverException;
 312             local = clientException;
 313         } else {
 314             remote = clientException;
 315             local = serverException;
 316         }
 317 
 318         Exception exception = null;
 319 
 320         /*
 321          * Check various exception conditions.
 322          */
 323         if ((local != null) && (remote != null)) {
 324             // If both failed, return the curthread's exception.
 325             local.initCause(remote);
 326             exception = local;
 327         } else if (local != null) {
 328             exception = local;
 329         } else if (remote != null) {
 330             exception = remote;
 331         } else if (startException != null) {
 332             exception = startException;
 333         }
 334 
 335         /*
 336          * If there was an exception *AND* a startException,
 337          * output it.
 338          */
 339         if (exception != null) {
 340             if (exception != startException && startException != null) {
 341                 exception.addSuppressed(startException);
 342             }
 343             throw exception;
 344         }
 345 
 346         // Fall-through: no exception to throw!
 347     }
 348 
 349     void startServer(boolean newThread) throws Exception {
 350         if (newThread) {
 351             serverThread = new Thread() {
 352                 @Override
 353                 public void run() {
 354                     try {
 355                         doServerSide();
 356                     } catch (Exception e) {
 357                         /*
 358                          * Our server thread just died.
 359                          *
 360                          * Release the client, if not active already...
 361                          */
 362                         System.out.println("Server died: " + e);
 363                         serverException = e;
 364                     }
 365                 }
 366             };
 367             serverThread.start();
 368         } else {
 369             try {
 370                 doServerSide();
 371             } catch (Exception e) {
 372                 System.out.println("Server failed: " + e);
 373                 serverException = e;
 374             }
 375         }
 376     }
 377 
 378     void startClient(boolean newThread) throws Exception {
 379         if (newThread) {
 380             clientThread = new Thread() {
 381                 @Override
 382                 public void run() {
 383                     try {
 384                         doClientSide();
 385                     } catch (Exception e) {
 386                         /*
 387                          * Our client thread just died.
 388                          */
 389                         System.out.println("Client died: " + e);
 390                         clientException = e;
 391                     }
 392                 }
 393             };
 394             clientThread.start();
 395         } else {
 396             try {
 397                 doClientSide();
 398             } catch (Exception e) {
 399                 System.out.println("Client failed: " + e);
 400                 clientException = e;
 401             }
 402         }
 403     }
 404 }