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