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