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 }