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