1 /*
   2  * Copyright (c) 2012, 2014, 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 7068321
  32  * @summary Support TLS Server Name Indication (SNI) Extension in JSSE Server
  33  * @library ../SSLEngine ../templates
  34  * @build SSLEngineService SSLCapabilities SSLExplorer
  35  * @run main/othervm SSLEngineExplorer SSLv2Hello,SSLv3
  36  * @run main/othervm SSLEngineExplorer SSLv3
  37  * @run main/othervm SSLEngineExplorer TLSv1
  38  * @run main/othervm SSLEngineExplorer TLSv1.1
  39  * @run main/othervm SSLEngineExplorer TLSv1.2
  40  */
  41 
  42 import javax.net.ssl.*;
  43 import java.nio.*;
  44 import java.net.*;
  45 import java.util.*;
  46 import java.nio.channels.*;
  47 import java.security.Security;
  48 
  49 public class SSLEngineExplorer extends SSLEngineService {
  50 
  51     /*
  52      * =============================================================
  53      * Set the various variables needed for the tests, then
  54      * specify what tests to run on each side.
  55      */
  56 
  57     /*
  58      * Should we run the client or server in a separate thread?
  59      * Both sides can throw exceptions, but do you have a preference
  60      * as to which side should be the main thread.
  61      */
  62     static boolean separateServerThread = true;
  63 
  64     // Is the server ready to serve?
  65     volatile static boolean serverReady = false;
  66 
  67     /*
  68      * Turn on SSL debugging?
  69      */
  70     static boolean debug = false;
  71 
  72     /*
  73      * Define the server side of the test.
  74      *
  75      * If the server prematurely exits, serverReady will be set to true
  76      * to avoid infinite hangs.
  77      */
  78     void doServerSide() throws Exception {
  79 
  80         // create SSLEngine.
  81         SSLEngine ssle = createSSLEngine(false);
  82 
  83         // Create a server socket channel.
  84         InetSocketAddress isa =
  85                 new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
  86         ServerSocketChannel ssc = ServerSocketChannel.open();
  87         ssc.socket().bind(isa);
  88         serverPort = ssc.socket().getLocalPort();
  89 
  90         // Signal Client, we're ready for his connect.
  91         serverReady = true;
  92 
  93         // Accept a socket channel.
  94         SocketChannel sc = ssc.accept();
  95         sc.configureBlocking(false);
  96 
  97         // Complete connection.
  98         while (!sc.finishConnect()) {
  99             Thread.sleep(50);
 100             // waiting for the connection completed.
 101         }
 102 
 103         ByteBuffer buffer = ByteBuffer.allocate(0xFF);
 104         int position = 0;
 105         SSLCapabilities capabilities = null;
 106 
 107         // Read the header of TLS record
 108         buffer.limit(SSLExplorer.RECORD_HEADER_SIZE);
 109         while (position < SSLExplorer.RECORD_HEADER_SIZE) {
 110             int n = sc.read(buffer);
 111             if (n < 0) {
 112                 throw new Exception("unexpected end of stream!");
 113             }
 114             position += n;
 115         }
 116         buffer.flip();
 117 
 118         int recordLength = SSLExplorer.getRequiredSize(buffer);
 119         if (buffer.capacity() < recordLength) {
 120             ByteBuffer oldBuffer = buffer;
 121             buffer = ByteBuffer.allocate(recordLength);
 122             buffer.put(oldBuffer);
 123         }
 124 
 125         buffer.position(SSLExplorer.RECORD_HEADER_SIZE);
 126         buffer.limit(buffer.capacity());
 127         while (position < recordLength) {
 128             int n = sc.read(buffer);
 129             if (n < 0) {
 130                 throw new Exception("unexpected end of stream!");
 131             }
 132             position += n;
 133         }
 134         buffer.flip();
 135 
 136         capabilities = SSLExplorer.explore(buffer);
 137         if (capabilities != null) {
 138             System.out.println("Record version: " +
 139                     capabilities.getRecordVersion());
 140             System.out.println("Hello version: " +
 141                     capabilities.getHelloVersion());
 142         }
 143 
 144         // handshaking
 145         handshaking(ssle, sc, buffer);
 146 
 147         // receive application data
 148         receive(ssle, sc);
 149 
 150         // send out application data
 151         deliver(ssle, sc);
 152 
 153         ExtendedSSLSession session = (ExtendedSSLSession)ssle.getSession();
 154         checkCapabilities(capabilities, session);
 155 
 156         // close the socket channel.
 157         sc.close();
 158         ssc.close();
 159     }
 160 
 161     /*
 162      * Define the client side of the test.
 163      *
 164      * If the server prematurely exits, serverReady will be set to true
 165      * to avoid infinite hangs.
 166      */
 167     void doClientSide() throws Exception {
 168         // create SSLEngine.
 169         SSLEngine ssle = createSSLEngine(true);
 170 
 171         /*
 172          * Wait for server to get started.
 173          */
 174         while (!serverReady) {
 175             Thread.sleep(50);
 176         }
 177 
 178         // Create a non-blocking socket channel.
 179         SocketChannel sc = SocketChannel.open();
 180         sc.configureBlocking(false);
 181         InetSocketAddress isa =
 182                 new InetSocketAddress(InetAddress.getLocalHost(), serverPort);
 183         sc.connect(isa);
 184 
 185         // Complete connection.
 186         while (!sc.finishConnect() ) {
 187             Thread.sleep(50);
 188             // waiting for the connection completed.
 189         }
 190 
 191         // enable the specified TLS protocol
 192         ssle.setEnabledProtocols(supportedProtocols);
 193 
 194         // handshaking
 195         handshaking(ssle, sc, null);
 196 
 197         // send out application data
 198         deliver(ssle, sc);
 199 
 200         // receive application data
 201         receive(ssle, sc);
 202 
 203         // close the socket channel.
 204         sc.close();
 205     }
 206 
 207     void checkCapabilities(SSLCapabilities capabilities,
 208             ExtendedSSLSession session) throws Exception {
 209 
 210         List<SNIServerName> sessionSNI = session.getRequestedServerNames();
 211         if (!sessionSNI.equals(capabilities.getServerNames())) {
 212             throw new Exception(
 213                     "server name indication does not match capabilities");
 214         }
 215     }
 216 
 217     private static String[] supportedProtocols;    // supported protocols
 218 
 219     private static void parseArguments(String[] args) {
 220         supportedProtocols = args[0].split(",");
 221     }
 222 
 223 
 224     /*
 225      * =============================================================
 226      * The remainder is just support stuff
 227      */
 228     volatile Exception serverException = null;
 229     volatile Exception clientException = null;
 230 
 231     // use any free port by default
 232     volatile int serverPort = 0;
 233 
 234     public static void main(String args[]) throws Exception {
 235         // reset the security property to make sure that the algorithms
 236         // and keys used in this test are not disabled.
 237         Security.setProperty("jdk.tls.disabledAlgorithms", "");
 238 
 239         if (debug)
 240             System.setProperty("javax.net.debug", "all");
 241 
 242         /*
 243          * Get the customized arguments.
 244          */
 245         parseArguments(args);
 246 
 247         new SSLEngineExplorer();
 248     }
 249 
 250     Thread clientThread = null;
 251     Thread serverThread = null;
 252 
 253     /*
 254      * Primary constructor, used to drive remainder of the test.
 255      *
 256      * Fork off the other side, then do your work.
 257      */
 258     SSLEngineExplorer() throws Exception {
 259         super("../etc");
 260 
 261         if (separateServerThread) {
 262             startServer(true);
 263             startClient(false);
 264         } else {
 265             startClient(true);
 266             startServer(false);
 267         }
 268 
 269         /*
 270          * Wait for other side to close down.
 271          */
 272         if (separateServerThread) {
 273             serverThread.join();
 274         } else {
 275             clientThread.join();
 276         }
 277 
 278         /*
 279          * When we get here, the test is pretty much over.
 280          *
 281          * If the main thread excepted, that propagates back
 282          * immediately.  If the other thread threw an exception, we
 283          * should report back.
 284          */
 285         if (serverException != null) {
 286             System.out.print("Server Exception:");
 287             throw serverException;
 288         }
 289         if (clientException != null) {
 290             System.out.print("Client Exception:");
 291             throw clientException;
 292         }
 293     }
 294 
 295     void startServer(boolean newThread) throws Exception {
 296         if (newThread) {
 297             serverThread = new Thread() {
 298                 public void run() {
 299                     try {
 300                         doServerSide();
 301                     } catch (Exception e) {
 302                         /*
 303                          * Our server thread just died.
 304                          *
 305                          * Release the client, if not active already...
 306                          */
 307                         System.err.println("Server died...");
 308                         System.err.println(e);
 309                         serverReady = true;
 310                         serverException = e;
 311                     }
 312                 }
 313             };
 314             serverThread.start();
 315         } else {
 316             doServerSide();
 317         }
 318     }
 319 
 320     void startClient(boolean newThread) throws Exception {
 321         if (newThread) {
 322             clientThread = new Thread() {
 323                 public void run() {
 324                     try {
 325                         doClientSide();
 326                     } catch (Exception e) {
 327                         /*
 328                          * Our client thread just died.
 329                          */
 330                         System.err.println("Client died...");
 331                         clientException = e;
 332                     }
 333                 }
 334             };
 335             clientThread.start();
 336         } else {
 337             doClientSide();
 338         }
 339     }
 340 }