1 /*
   2  * Copyright (c) 2005, 2011, 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 // SunJSSE does not support dynamic system properties, no way to re-use
  25 // system properties in samevm/agentvm mode.
  26 
  27 /*
  28  * @test
  29  * @bug 4836493
  30  * @ignore need further evaluation
  31  * @summary Socket timeouts for SSLSockets causes data corruption.
  32  * @run main/othervm ClientTimeout
  33  */
  34 
  35 import java.io.*;
  36 import java.net.*;
  37 import java.util.*;
  38 import java.security.*;
  39 import javax.net.ssl.*;
  40 
  41 public class ClientTimeout {
  42 
  43     /*
  44      * =============================================================
  45      * Set the various variables needed for the tests, then
  46      * specify what tests to run on each side.
  47      */
  48 
  49     /*
  50      * Should we run the client or server in a separate thread?
  51      * Both sides can throw exceptions, but do you have a preference
  52      * as to which side should be the main thread.
  53      */
  54     static boolean separateServerThread = true;
  55 
  56     /*
  57      * Where do we find the keystores?
  58      */
  59     static String pathToStores = "../../../../javax/net/ssl/etc";
  60     static String keyStoreFile = "keystore";
  61     static String trustStoreFile = "truststore";
  62     static String passwd = "passphrase";
  63 
  64     /*
  65      * Is the server ready to serve?
  66      */
  67     volatile static boolean serverReady = false;
  68 
  69     /*
  70      * Turn on SSL debugging?
  71      */
  72     static boolean debug = false;
  73 
  74 
  75     /*
  76      * define the rhythm of timeout exception
  77      */
  78     static boolean rhythm = true;
  79 
  80     /*
  81      * If the client or server is doing some kind of object creation
  82      * that the other side depends on, and that thread prematurely
  83      * exits, you may experience a hang.  The test harness will
  84      * terminate all hung threads after its timeout has expired,
  85      * currently 3 minutes by default, but you might try to be
  86      * smart about it....
  87      */
  88 
  89     /*
  90      * Define the server side of the test.
  91      *
  92      * If the server prematurely exits, serverReady will be set to true
  93      * to avoid infinite hangs.
  94      */
  95     void doServerSide() throws Exception {
  96         SSLServerSocketFactory sslssf =
  97             (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
  98         SSLServerSocket sslServerSocket =
  99             (SSLServerSocket) sslssf.createServerSocket(serverPort);
 100 
 101         serverPort = sslServerSocket.getLocalPort();
 102 
 103         /*
 104          * Signal Client, we're ready for his connect.
 105          */
 106         serverReady = true;
 107 
 108         SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
 109         InputStream sslIS = sslSocket.getInputStream();
 110         OutputStream sslOS = sslSocket.getOutputStream();
 111         sslSocket.startHandshake();
 112 
 113         // transfer a file to client.
 114         String transFilename =
 115             System.getProperty("test.src", "./") + "/" +
 116                         this.getClass().getName() + ".java";
 117         MessageDigest md = MessageDigest.getInstance("SHA");
 118         DigestInputStream transIns = new DigestInputStream(
 119                 new FileInputStream(transFilename), md);
 120 
 121         byte[] bytes = new byte[2000];
 122         int i = 0;
 123         while (true) {
 124             // reset the cycle
 125             if (i >= bytes.length) {
 126                 i = 0;
 127             }
 128 
 129             int length = 0;
 130             if ((length = transIns.read(bytes, 0, i++)) == -1) {
 131                 break;
 132             } else {
 133                 sslOS.write(bytes, 0, length);
 134                 sslOS.flush();
 135 
 136                 if (i % 3 == 0) {
 137                     Thread.sleep(300);  // Stall past the timeout...
 138                 }
 139             }
 140         }
 141         serverDigest = md.digest();
 142         transIns.close();
 143         sslSocket.close();
 144     }
 145 
 146     /*
 147      * Define the client side of the test.
 148      *
 149      * If the server prematurely exits, serverReady will be set to true
 150      * to avoid infinite hangs.
 151      */
 152     void doClientSide() throws Exception {
 153         boolean caught = false;
 154 
 155         /*
 156          * Wait for server to get started.
 157          */
 158         while (!serverReady) {
 159             Thread.sleep(50);
 160         }
 161 
 162         Socket baseSocket = new Socket("localhost", serverPort) {
 163             MyInputStream ins = null;
 164 
 165             public InputStream getInputStream() throws IOException {
 166                 if (ins != null) {
 167                     return ins;
 168                 } else {
 169                     ins = new MyInputStream(super.getInputStream());
 170                     return ins;
 171                 }
 172             }
 173         };
 174 
 175         SSLSocketFactory sslsf =
 176             (SSLSocketFactory) SSLSocketFactory.getDefault();
 177         SSLSocket sslSocket = (SSLSocket)
 178             sslsf.createSocket(baseSocket, "localhost", serverPort, true);
 179 
 180         InputStream sslIS = sslSocket.getInputStream();
 181         OutputStream sslOS = sslSocket.getOutputStream();
 182 
 183         // handshaking
 184         sslSocket.setSoTimeout(100); // The stall timeout.
 185         while (true) {
 186             try {
 187                 rhythm = true;
 188                 sslSocket.startHandshake();
 189                 break;
 190             } catch (SocketTimeoutException e) {
 191                 System.out.println("Handshaker exception: "
 192                         + e.getMessage());
 193             }
 194         }
 195 
 196         // read application data from server
 197         MessageDigest md = MessageDigest.getInstance("SHA");
 198         DigestInputStream transIns = new DigestInputStream(sslIS, md);
 199         byte[] bytes = new byte[2000];
 200         while (true) {
 201             try {
 202                 rhythm = true;
 203 
 204                 while (transIns.read(bytes, 0, 17) != -1) {
 205                     rhythm = true;
 206                 }
 207                 break;
 208             } catch (SocketTimeoutException e) {
 209                 System.out.println("InputStream Exception: "
 210                         + e.getMessage());
 211             }
 212         }
 213         // Wait for server to get ready.
 214         while (serverDigest == null) {
 215             Thread.sleep(20);
 216         }
 217 
 218         byte[] cliDigest = md.digest();
 219         if (!Arrays.equals(cliDigest, serverDigest)) {
 220             throw new Exception("Application data trans error");
 221         }
 222 
 223         transIns.close();
 224         sslSocket.close();
 225     }
 226 
 227     /*
 228      * =============================================================
 229      * The remainder is just support stuff
 230      */
 231 
 232     static class MyInputStream extends InputStream {
 233         InputStream ins = null;
 234 
 235         public MyInputStream(InputStream ins) {
 236             this.ins = ins;
 237         }
 238 
 239         public int read() throws IOException {
 240             return read(new byte[1], 0, 1);
 241         }
 242 
 243         public int read(byte[] data, int offset, int len) throws IOException {
 244             if (!ClientTimeout.rhythm) {
 245                 throw new SocketTimeoutException(
 246                         "Throwing a timeout exception");
 247             }
 248             ClientTimeout.rhythm = false;
 249             return ins.read(data, offset, len);
 250         }
 251     }
 252 
 253     // use any free port by default
 254     volatile int serverPort = 0;
 255 
 256     volatile Exception serverException = null;
 257     volatile Exception clientException = null;
 258 
 259     volatile byte[] serverDigest = null;
 260 
 261     public static void main(String[] args) throws Exception {
 262         String keyFilename =
 263             System.getProperty("test.src", "./") + "/" + pathToStores +
 264                 "/" + keyStoreFile;
 265         String trustFilename =
 266             System.getProperty("test.src", "./") + "/" + pathToStores +
 267                 "/" + trustStoreFile;
 268 
 269         System.setProperty("javax.net.ssl.keyStore", keyFilename);
 270         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
 271         System.setProperty("javax.net.ssl.trustStore", trustFilename);
 272         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
 273 
 274         if (debug)
 275             System.setProperty("javax.net.debug", "all");
 276 
 277         /*
 278          * Start the tests.
 279          */
 280         new ClientTimeout();
 281     }
 282 
 283     Thread clientThread = null;
 284     Thread serverThread = null;
 285 
 286     /*
 287      * Primary constructor, used to drive remainder of the test.
 288      *
 289      * Fork off the other side, then do your work.
 290      */
 291     ClientTimeout() throws Exception {
 292         if (separateServerThread) {
 293             startServer(true);
 294             startClient(false);
 295         } else {
 296             startClient(true);
 297             startServer(false);
 298         }
 299 
 300         /*
 301          * Wait for other side to close down.
 302          */
 303         if (separateServerThread) {
 304             serverThread.join();
 305         } else {
 306             clientThread.join();
 307         }
 308 
 309         /*
 310          * When we get here, the test is pretty much over.
 311          *
 312          * If the main thread excepted, that propagates back
 313          * immediately.  If the other thread threw an exception, we
 314          * should report back.
 315          */
 316         if (serverException != null) {
 317             System.out.print("Server Exception:");
 318             throw serverException;
 319         }
 320         if (clientException != null) {
 321             System.out.print("Client Exception:");
 322             throw clientException;
 323         }
 324     }
 325 
 326     void startServer(boolean newThread) throws Exception {
 327         if (newThread) {
 328             serverThread = new Thread() {
 329                 public void run() {
 330                     try {
 331                         doServerSide();
 332                     } catch (Exception e) {
 333                         /*
 334                          * Our server thread just died.
 335                          *
 336                          * Release the client, if not active already...
 337                          */
 338                         System.err.println("Server died...");
 339                         System.err.println(e);
 340                         serverReady = true;
 341                         serverException = e;
 342                     }
 343                 }
 344             };
 345             serverThread.start();
 346         } else {
 347             doServerSide();
 348         }
 349     }
 350 
 351     void startClient(boolean newThread) throws Exception {
 352         if (newThread) {
 353             clientThread = new Thread() {
 354                 public void run() {
 355                     try {
 356                         doClientSide();
 357                     } catch (Exception e) {
 358                         /*
 359                          * Our client thread just died.
 360                          */
 361                         System.err.println("Client died...");
 362                         clientException = e;
 363                     }
 364                 }
 365             };
 366             clientThread.start();
 367         } else {
 368             doClientSide();
 369         }
 370     }
 371 }