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