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 }